├── .gitignore
├── README.md
├── build.gradle
├── demo
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── cn
│ │ └── bingoogolapple
│ │ └── swipeitemlayout
│ │ └── demo
│ │ ├── activity
│ │ ├── ListViewDemoActivity.java
│ │ ├── MainActivity.java
│ │ ├── RecyclerViewDemoActivity.java
│ │ └── SwipeItemActivity.java
│ │ ├── adapter
│ │ ├── AdapterViewAdapter.java
│ │ └── RecyclerViewAdapter.java
│ │ ├── engine
│ │ └── DataEngine.java
│ │ ├── model
│ │ └── NormalModel.java
│ │ └── widget
│ │ └── Divider.java
│ └── res
│ ├── drawable
│ ├── selector_btn_orange.xml
│ ├── selector_btn_red.xml
│ └── selector_item.xml
│ ├── layout
│ ├── activity_listview.xml
│ ├── activity_main.xml
│ ├── activity_recyclerview.xml
│ ├── activity_swipeitem.xml
│ ├── item_bgaswipe.xml
│ ├── item_bgaswipe_bak.xml
│ └── item_bgaswipe_bak_gap.xml
│ ├── mipmap-hdpi
│ ├── avator.png
│ ├── delete.png
│ ├── ic_launcher.png
│ └── list_divider.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── values-v21
│ └── styles.xml
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ ├── styles.xml
│ └── styles_base.xml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
├── build.gradle
├── gradle.properties
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── cn
│ │ └── bingoogolapple
│ │ └── swipeitemlayout
│ │ └── BGASwipeItemLayout.java
│ └── res
│ └── values
│ └── attrs.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Gradle files
16 | .gradle/
17 | build/
18 | /*/build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Eclipse project files
30 | .classpath
31 | .project
32 | .settings/
33 |
34 | # Intellij project files
35 | *.iml
36 | *.ipr
37 | *.iws
38 | .idea/
39 |
40 | # Mac system files
41 | .DS_Store
42 |
43 | *.keystore
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | :running:BGASwipeItemLayout-Android:running:
2 | ============
3 |
4 | [](https://www.apache.org/licenses/LICENSE-2.0)
5 | [](https://maven-badges.herokuapp.com/maven-central/cn.bingoogolapple/bga-swipeitemlayout)
6 |
7 | 类似iOS带弹簧效果的左右滑动控件,可作为AbsListView和RecyclerView的item。支持给BGASwipeItemLayout和其子控件设置margin和padding属性
8 |
9 | ### 效果图
10 | 
11 | 
12 | 
13 |
14 | ### Gradle依赖
15 |
16 | ```groovy
17 | dependencies {
18 | compile 'com.android.support:support-v4:latestVersion'
19 | compile 'cn.bingoogolapple:bga-swipeitemlayout:latestVersion@aar'
20 | }
21 | ```
22 |
23 | ### BGASwipeItemLayout方法说明
24 |
25 | ```java
26 | /**
27 | * 以动画方式打开
28 | */
29 | public void openWithAnim()
30 |
31 | /**
32 | * 以动画方式关闭
33 | */
34 | public void closeWithAnim()
35 |
36 | /**
37 | * 直接打开
38 | */
39 | public void open()
40 |
41 | /**
42 | * 直接关闭。如果在AbsListView中删除已经打开的item时,请用该方法关闭item,否则重用item时有问题。RecyclerView中可以用该方法,也可以用closeWithAnim
43 | */
44 | public void close()
45 |
46 | /**
47 | * 当前是否为打开状态
48 | *
49 | * @return
50 | */
51 | public boolean isOpened()
52 |
53 | /**
54 | * 当前是否为关闭状态
55 | *
56 | * @return
57 | */
58 | public boolean isClosed()
59 |
60 | /**
61 | * 获取顶部视图
62 | *
63 | * @return
64 | */
65 | public View getTopView()
66 |
67 | /**
68 | * 获取底部视图
69 | *
70 | * @return
71 | */
72 | public View getBottomView()
73 |
74 | /**
75 | * 设置是否可滑动
76 | *
77 | * @return
78 | */
79 | public void setSwipeAble(boolean swipeAble)
80 | ```
81 |
82 | ### BGASwipeItemLayoutDelegate接口说明
83 |
84 | ```java
85 | /**
86 | * 变为打开状态
87 | *
88 | * @param swipeItemLayout
89 | */
90 | void onBGASwipeItemLayoutOpened(BGASwipeItemLayout swipeItemLayout);
91 |
92 | /**
93 | * 变为关闭状态
94 | *
95 | * @param swipeItemLayout
96 | */
97 | void onBGASwipeItemLayoutClosed(BGASwipeItemLayout swipeItemLayout);
98 |
99 | /**
100 | * 从关闭状态切换到正在打开状态
101 | *
102 | * @param swipeItemLayout
103 | */
104 | void onBGASwipeItemLayoutStartOpen(BGASwipeItemLayout swipeItemLayout);
105 | ```
106 |
107 | ### 自定义属性说明
108 |
109 | 属性名 | 说明 | 默认值
110 | :----------- | :----------- | :-----------
111 | bga_sil_swipeDirection | 往左滑还是往右滑为打开状态(left或right) | left
112 | bga_sil_bottomMode | 底部视图展现方式(layDown或pullOut) | pullOut
113 | bga_sil_springDistance | 弹簧距离 | 0dp
114 | bga_sil_swipeAble | 是否可左右滑动 | true
115 |
116 | ## 作者联系方式
117 |
118 | | 个人主页 | 邮箱 |
119 | | ------------- | ------------ |
120 | | bingoogolapple.cn | bingoogolapple@gmail.com |
121 |
122 | | 个人微信号 | 微信群 | 公众号 |
123 | | ------------ | ------------ | ------------ |
124 | |
|
|
|
125 |
126 | | 个人 QQ 号 | QQ 群 |
127 | | ------------ | ------------ |
128 | |
|
|
129 |
130 | ## 打赏支持作者
131 |
132 | 如果您觉得 BGA 系列开源库或工具软件帮您节省了大量的开发时间,可以扫描下方的二维码打赏支持。您的支持将鼓励我继续创作,打赏后还可以加我微信免费开通一年 [上帝小助手浏览器扩展/插件开发平台](https://github.com/bingoogolapple/bga-god-assistant-config) 的会员服务
133 |
134 | | 微信 | QQ | 支付宝 |
135 | | ------------- | ------------- | ------------- |
136 | |
|
|
|
137 |
138 | ## 作者项目推荐
139 |
140 | * 欢迎您使用我开发的第一个独立开发软件产品 [上帝小助手浏览器扩展/插件开发平台](https://github.com/bingoogolapple/bga-god-assistant-config)
141 |
142 | ## License
143 |
144 | Copyright 2015 bingoogolapple
145 |
146 | Licensed under the Apache License, Version 2.0 (the "License");
147 | you may not use this file except in compliance with the License.
148 | You may obtain a copy of the License at
149 |
150 | http://www.apache.org/licenses/LICENSE-2.0
151 |
152 | Unless required by applicable law or agreed to in writing, software
153 | distributed under the License is distributed on an "AS IS" BASIS,
154 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
155 | See the License for the specific language governing permissions and
156 | limitations under the License.
157 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/demo/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion ANDROID_BUILD_SDK_VERSION as int
5 | buildToolsVersion ANDROID_BUILD_TOOLS_VERSION
6 |
7 | defaultConfig {
8 | minSdkVersion ANDROID_BUILD_MIN_SDK_VERSION as int
9 | targetSdkVersion ANDROID_BUILD_TARGET_SDK_VERSION as int
10 | versionCode VERSION_CODE as int
11 | versionName VERSION_NAME
12 | }
13 | }
14 |
15 | dependencies {
16 | // -------------------- 以下两个库是必须依赖的 ----------------------------
17 | compile 'com.android.support:support-v4:25.0.1'
18 | compile 'cn.bingoogolapple:bga-swipeitemlayout:1.0.4@aar'
19 | // -------------------- 以上两个库是必须依赖的 ----------------------------
20 |
21 |
22 |
23 | // 以下几个库是演示案例所使用到的
24 | // compile project(':library')
25 | compile 'com.android.support:appcompat-v7:25.0.1'
26 | compile 'com.android.support:recyclerview-v7:25.0.1'
27 | compile 'cn.bingoogolapple:bga-adapter:1.1.5@aar'
28 | }
29 |
--------------------------------------------------------------------------------
/demo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
24 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/demo/src/main/java/cn/bingoogolapple/swipeitemlayout/demo/activity/ListViewDemoActivity.java:
--------------------------------------------------------------------------------
1 | package cn.bingoogolapple.swipeitemlayout.demo.activity;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.AbsListView;
8 | import android.widget.AdapterView;
9 | import android.widget.ListView;
10 | import android.widget.Toast;
11 |
12 | import java.util.List;
13 |
14 | import cn.bingoogolapple.androidcommon.adapter.BGAOnItemChildClickListener;
15 | import cn.bingoogolapple.androidcommon.adapter.BGAOnItemChildLongClickListener;
16 | import cn.bingoogolapple.swipeitemlayout.demo.R;
17 | import cn.bingoogolapple.swipeitemlayout.demo.adapter.AdapterViewAdapter;
18 | import cn.bingoogolapple.swipeitemlayout.demo.engine.DataEngine;
19 | import cn.bingoogolapple.swipeitemlayout.demo.model.NormalModel;
20 |
21 | /**
22 | * 作者:王浩 邮件:bingoogolapple@gmail.com
23 | * 创建时间:15/5/22 10:06
24 | * 描述:
25 | */
26 | public class ListViewDemoActivity extends AppCompatActivity implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, BGAOnItemChildClickListener, BGAOnItemChildLongClickListener {
27 | private static final String TAG = ListViewDemoActivity.class.getSimpleName();
28 | private List mData;
29 | private ListView mDataLv;
30 | private AdapterViewAdapter mAdapter;
31 |
32 | @Override
33 | protected void onCreate(Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 | setContentView(R.layout.activity_listview);
36 |
37 | initListView();
38 | }
39 |
40 | private void initListView() {
41 | mDataLv = (ListView) findViewById(R.id.lv_listview_data);
42 | mDataLv.setOnItemClickListener(this);
43 | mDataLv.setOnItemLongClickListener(this);
44 |
45 | mAdapter = new AdapterViewAdapter(this);
46 | mAdapter.setOnItemChildClickListener(this);
47 | mAdapter.setOnItemChildLongClickListener(this);
48 |
49 | mDataLv.setOnScrollListener(new AbsListView.OnScrollListener() {
50 | @Override
51 | public void onScrollStateChanged(AbsListView view, int scrollState) {
52 | if (AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL == scrollState) {
53 | mAdapter.closeOpenedSwipeItemLayoutWithAnim();
54 | }
55 | }
56 |
57 | @Override
58 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
59 | }
60 | });
61 |
62 | mData = DataEngine.loadNormalModelDatas();
63 | mAdapter.setData(mData);
64 | mDataLv.setAdapter(mAdapter);
65 | }
66 |
67 | @Override
68 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
69 | Toast.makeText(this, "点击了条目 " + mAdapter.getItem(position).mTitle, Toast.LENGTH_SHORT).show();
70 | }
71 |
72 | @Override
73 | public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) {
74 | Toast.makeText(this, "长按了" + mAdapter.getItem(position).mTitle, Toast.LENGTH_SHORT).show();
75 | return true;
76 | }
77 |
78 | @Override
79 | public void onItemChildClick(ViewGroup parent, View childView, int position) {
80 | if (childView.getId() == R.id.tv_item_bgaswipe_delete) {
81 | // 作为ListView的item使用时,如果删除了某一个item,请先关闭已经打开的item,否则其他item会显示不正常(RecyclerView不会有这个问题)
82 | mAdapter.closeOpenedSwipeItemLayout();
83 | mAdapter.removeItem(position);
84 | }
85 | }
86 |
87 | @Override
88 | public boolean onItemChildLongClick(ViewGroup parent, View childView, int position) {
89 | if (childView.getId() == R.id.tv_item_bgaswipe_delete) {
90 | Toast.makeText(this, "长按了删除 " + mAdapter.getItem(position).mTitle, Toast.LENGTH_SHORT).show();
91 | return true;
92 | }
93 | return false;
94 | }
95 | }
--------------------------------------------------------------------------------
/demo/src/main/java/cn/bingoogolapple/swipeitemlayout/demo/activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cn.bingoogolapple.swipeitemlayout.demo.activity;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 |
8 | import cn.bingoogolapple.swipeitemlayout.demo.R;
9 |
10 | /**
11 | * 作者:王浩 邮件:bingoogolapple@gmail.com
12 | * 创建时间:15/5/28 10:23
13 | * 描述:
14 | */
15 | public class MainActivity extends AppCompatActivity {
16 | private static final String TAG = MainActivity.class.getSimpleName();
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | setContentView(R.layout.activity_main);
22 | }
23 |
24 | public void changeToSwipeItemDemo(View v) {
25 | startActivity(new Intent(this, SwipeItemActivity.class));
26 | }
27 |
28 | public void changeToListViewDemo(View v) {
29 | startActivity(new Intent(this, ListViewDemoActivity.class));
30 | }
31 |
32 | public void changeToRecyclerViewDemo(View v) {
33 | startActivity(new Intent(this, RecyclerViewDemoActivity.class));
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/demo/src/main/java/cn/bingoogolapple/swipeitemlayout/demo/activity/RecyclerViewDemoActivity.java:
--------------------------------------------------------------------------------
1 | package cn.bingoogolapple.swipeitemlayout.demo.activity;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.support.v7.widget.LinearLayoutManager;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.Toast;
10 |
11 | import java.util.List;
12 |
13 | import cn.bingoogolapple.androidcommon.adapter.BGAOnItemChildClickListener;
14 | import cn.bingoogolapple.androidcommon.adapter.BGAOnItemChildLongClickListener;
15 | import cn.bingoogolapple.androidcommon.adapter.BGAOnRVItemClickListener;
16 | import cn.bingoogolapple.androidcommon.adapter.BGAOnRVItemLongClickListener;
17 | import cn.bingoogolapple.swipeitemlayout.demo.R;
18 | import cn.bingoogolapple.swipeitemlayout.demo.adapter.RecyclerViewAdapter;
19 | import cn.bingoogolapple.swipeitemlayout.demo.engine.DataEngine;
20 | import cn.bingoogolapple.swipeitemlayout.demo.model.NormalModel;
21 | import cn.bingoogolapple.swipeitemlayout.demo.widget.Divider;
22 |
23 | /**
24 | * 作者:王浩 邮件:bingoogolapple@gmail.com
25 | * 创建时间:15/5/22 10:06
26 | * 描述:
27 | */
28 | public class RecyclerViewDemoActivity extends AppCompatActivity implements BGAOnRVItemClickListener, BGAOnRVItemLongClickListener, BGAOnItemChildClickListener, BGAOnItemChildLongClickListener {
29 | private static final String TAG = RecyclerViewDemoActivity.class.getSimpleName();
30 | private RecyclerViewAdapter mAdapter;
31 | private List mData;
32 | private RecyclerView mDataRv;
33 |
34 | @Override
35 | protected void onCreate(Bundle savedInstanceState) {
36 | super.onCreate(savedInstanceState);
37 | setContentView(R.layout.activity_recyclerview);
38 |
39 | initRecyclerView();
40 | }
41 |
42 | private void initRecyclerView() {
43 | mDataRv = (RecyclerView) findViewById(R.id.rv_recyclerview_data);
44 | mDataRv.addItemDecoration(new Divider(this));
45 |
46 | LinearLayoutManager layoutManager = new LinearLayoutManager(this);
47 | layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
48 | mDataRv.setLayoutManager(layoutManager);
49 |
50 | mAdapter = new RecyclerViewAdapter(mDataRv);
51 | mAdapter.setOnRVItemClickListener(this);
52 | mAdapter.setOnRVItemLongClickListener(this);
53 | mAdapter.setOnItemChildClickListener(this);
54 | mAdapter.setOnItemChildLongClickListener(this);
55 |
56 | mDataRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
57 | @Override
58 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
59 | if (RecyclerView.SCROLL_STATE_DRAGGING == newState) {
60 | mAdapter.closeOpenedSwipeItemLayoutWithAnim();
61 | }
62 | }
63 | });
64 |
65 | mData = DataEngine.loadNormalModelDatas();
66 | mAdapter.setData(mData);
67 | mDataRv.setAdapter(mAdapter);
68 | }
69 |
70 | @Override
71 | public void onItemChildClick(ViewGroup parent, View childView, int position) {
72 | if (childView.getId() == R.id.tv_item_bgaswipe_delete) {
73 | mAdapter.closeOpenedSwipeItemLayoutWithAnim();
74 | mAdapter.removeItem(position);
75 | }
76 | }
77 |
78 | @Override
79 | public boolean onItemChildLongClick(ViewGroup parent, View childView, int position) {
80 | if (childView.getId() == R.id.tv_item_bgaswipe_delete) {
81 | Toast.makeText(this, "长按了删除 " + mAdapter.getItem(position).mTitle, Toast.LENGTH_SHORT).show();
82 | return true;
83 | }
84 | return false;
85 | }
86 |
87 | @Override
88 | public void onRVItemClick(ViewGroup parent, View itemView, int position) {
89 | Toast.makeText(this, "点击了条目 " + mAdapter.getItem(position).mTitle, Toast.LENGTH_SHORT).show();
90 | }
91 |
92 | @Override
93 | public boolean onRVItemLongClick(ViewGroup parent, View itemView, int position) {
94 | Toast.makeText(this, "长按了条目 " + mAdapter.getItem(position).mTitle, Toast.LENGTH_SHORT).show();
95 | return true;
96 | }
97 | }
--------------------------------------------------------------------------------
/demo/src/main/java/cn/bingoogolapple/swipeitemlayout/demo/activity/SwipeItemActivity.java:
--------------------------------------------------------------------------------
1 | package cn.bingoogolapple.swipeitemlayout.demo.activity;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.view.View;
6 | import android.widget.Toast;
7 |
8 | import cn.bingoogolapple.swipeitemlayout.BGASwipeItemLayout;
9 | import cn.bingoogolapple.swipeitemlayout.demo.R;
10 |
11 | public class SwipeItemActivity extends AppCompatActivity {
12 | private BGASwipeItemLayout mTestSil;
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.activity_swipeitem);
18 | mTestSil = (BGASwipeItemLayout) findViewById(R.id.sil_swipeitem_test);
19 | findViewById(R.id.iv_swipeitem_avator).setOnLongClickListener(new View.OnLongClickListener() {
20 | @Override
21 | public boolean onLongClick(View v) {
22 | Toast.makeText(SwipeItemActivity.this, "长按了头像", Toast.LENGTH_SHORT).show();
23 | return true;
24 | }
25 | });
26 | findViewById(R.id.iv_swipeitem_delete).setOnLongClickListener(new View.OnLongClickListener() {
27 | @Override
28 | public boolean onLongClick(View v) {
29 | Toast.makeText(SwipeItemActivity.this, "长按了删除", Toast.LENGTH_SHORT).show();
30 | return true;
31 | }
32 | });
33 |
34 | mTestSil.setDelegate(new BGASwipeItemLayout.BGASwipeItemLayoutDelegate() {
35 | @Override
36 | public void onBGASwipeItemLayoutOpened(BGASwipeItemLayout swipeItemLayout) {
37 | Toast.makeText(SwipeItemActivity.this, "打开", Toast.LENGTH_SHORT).show();
38 | }
39 |
40 | @Override
41 | public void onBGASwipeItemLayoutClosed(BGASwipeItemLayout swipeItemLayout) {
42 | Toast.makeText(SwipeItemActivity.this, "关闭", Toast.LENGTH_SHORT).show();
43 | }
44 |
45 | @Override
46 | public void onBGASwipeItemLayoutStartOpen(BGASwipeItemLayout swipeItemLayout) {
47 | Toast.makeText(SwipeItemActivity.this, "开始打开", Toast.LENGTH_SHORT).show();
48 | }
49 |
50 | });
51 | }
52 |
53 | public void onClick(View view) {
54 | switch (view.getId()) {
55 | case R.id.iv_swipeitem_avator:
56 | Toast.makeText(this, "点击了头像", Toast.LENGTH_SHORT).show();
57 | break;
58 | case R.id.iv_swipeitem_delete:
59 | Toast.makeText(this, "点击了删除", Toast.LENGTH_SHORT).show();
60 | break;
61 | case R.id.btn_swipeitem_open:
62 | mTestSil.open();
63 | break;
64 | case R.id.btn_swipeitem_close:
65 | mTestSil.close();
66 | break;
67 | case R.id.btn_swipeitem_openwithanim:
68 | mTestSil.openWithAnim();
69 | break;
70 | case R.id.btn_swipeitem_closewithanim:
71 | mTestSil.closeWithAnim();
72 | break;
73 | case R.id.btn_swipeitem_status:
74 | showStatus();
75 | break;
76 | }
77 | }
78 |
79 | private void showStatus() {
80 | if (mTestSil.isOpened()) {
81 | Toast.makeText(this, "打开状态", Toast.LENGTH_SHORT).show();
82 | } else if (mTestSil.isClosed()) {
83 | Toast.makeText(this, "关闭状态", Toast.LENGTH_SHORT).show();
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/demo/src/main/java/cn/bingoogolapple/swipeitemlayout/demo/adapter/AdapterViewAdapter.java:
--------------------------------------------------------------------------------
1 | package cn.bingoogolapple.swipeitemlayout.demo.adapter;
2 |
3 | import android.content.Context;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | import cn.bingoogolapple.androidcommon.adapter.BGAAdapterViewAdapter;
9 | import cn.bingoogolapple.androidcommon.adapter.BGAViewHolderHelper;
10 | import cn.bingoogolapple.swipeitemlayout.BGASwipeItemLayout;
11 | import cn.bingoogolapple.swipeitemlayout.demo.R;
12 | import cn.bingoogolapple.swipeitemlayout.demo.model.NormalModel;
13 |
14 | /**
15 | * 作者:王浩 邮件:bingoogolapple@gmail.com
16 | * 创建时间:15/5/21 上午12:39
17 | * 描述:
18 | */
19 | public class AdapterViewAdapter extends BGAAdapterViewAdapter {
20 | /**
21 | * 当前处于打开状态的item
22 | */
23 | private List mOpenedSil = new ArrayList<>();
24 |
25 | public AdapterViewAdapter(Context context) {
26 | super(context, R.layout.item_bgaswipe);
27 | }
28 |
29 | @Override
30 | protected void setItemChildListener(BGAViewHolderHelper viewHolderHelper) {
31 | BGASwipeItemLayout swipeItemLayout = viewHolderHelper.getView(R.id.sil_item_bgaswipe_root);
32 | swipeItemLayout.setDelegate(new BGASwipeItemLayout.BGASwipeItemLayoutDelegate() {
33 | @Override
34 | public void onBGASwipeItemLayoutOpened(BGASwipeItemLayout swipeItemLayout) {
35 | closeOpenedSwipeItemLayoutWithAnim();
36 | mOpenedSil.add(swipeItemLayout);
37 | }
38 |
39 | @Override
40 | public void onBGASwipeItemLayoutClosed(BGASwipeItemLayout swipeItemLayout) {
41 | mOpenedSil.remove(swipeItemLayout);
42 | }
43 |
44 | @Override
45 | public void onBGASwipeItemLayoutStartOpen(BGASwipeItemLayout swipeItemLayout) {
46 | closeOpenedSwipeItemLayoutWithAnim();
47 | }
48 | });
49 | viewHolderHelper.setItemChildClickListener(R.id.tv_item_bgaswipe_delete);
50 | viewHolderHelper.setItemChildLongClickListener(R.id.tv_item_bgaswipe_delete);
51 | }
52 |
53 | @Override
54 | public void fillData(BGAViewHolderHelper viewHolderHelper, int position, NormalModel model) {
55 | viewHolderHelper.setText(R.id.tv_item_bgaswipe_title, model.mTitle).setText(R.id.tv_item_bgaswipe_detail, model.mDetail).setText(R.id.et_item_bgaswipe_title, model.mTitle);
56 |
57 | BGASwipeItemLayout swipeItemLayout = viewHolderHelper.getView(R.id.sil_item_bgaswipe_root);
58 | if (position % 3 == 0) {
59 | swipeItemLayout.setSwipeAble(false);
60 | } else {
61 | swipeItemLayout.setSwipeAble(true);
62 | }
63 | }
64 |
65 | public void closeOpenedSwipeItemLayoutWithAnim() {
66 | for (BGASwipeItemLayout sil : mOpenedSil) {
67 | sil.closeWithAnim();
68 | }
69 | mOpenedSil.clear();
70 | }
71 |
72 | public void closeOpenedSwipeItemLayout() {
73 | for (BGASwipeItemLayout sil : mOpenedSil) {
74 | sil.close();
75 | }
76 | mOpenedSil.clear();
77 | }
78 | }
--------------------------------------------------------------------------------
/demo/src/main/java/cn/bingoogolapple/swipeitemlayout/demo/adapter/RecyclerViewAdapter.java:
--------------------------------------------------------------------------------
1 | package cn.bingoogolapple.swipeitemlayout.demo.adapter;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | import cn.bingoogolapple.androidcommon.adapter.BGARecyclerViewAdapter;
9 | import cn.bingoogolapple.androidcommon.adapter.BGAViewHolderHelper;
10 | import cn.bingoogolapple.swipeitemlayout.BGASwipeItemLayout;
11 | import cn.bingoogolapple.swipeitemlayout.demo.R;
12 | import cn.bingoogolapple.swipeitemlayout.demo.model.NormalModel;
13 |
14 | /**
15 | * 作者:王浩 邮件:bingoogolapple@gmail.com
16 | * 创建时间:15/5/22 16:31
17 | * 描述:
18 | */
19 | public class RecyclerViewAdapter extends BGARecyclerViewAdapter {
20 | /**
21 | * 当前处于打开状态的item
22 | */
23 | private List mOpenedSil = new ArrayList<>();
24 |
25 | public RecyclerViewAdapter(RecyclerView recyclerView) {
26 | super(recyclerView, R.layout.item_bgaswipe);
27 | }
28 |
29 | @Override
30 | public void setItemChildListener(BGAViewHolderHelper viewHolderHelper, int viewType) {
31 | BGASwipeItemLayout swipeItemLayout = viewHolderHelper.getView(R.id.sil_item_bgaswipe_root);
32 | swipeItemLayout.setDelegate(new BGASwipeItemLayout.BGASwipeItemLayoutDelegate() {
33 | @Override
34 | public void onBGASwipeItemLayoutOpened(BGASwipeItemLayout swipeItemLayout) {
35 | closeOpenedSwipeItemLayoutWithAnim();
36 | mOpenedSil.add(swipeItemLayout);
37 | }
38 |
39 | @Override
40 | public void onBGASwipeItemLayoutClosed(BGASwipeItemLayout swipeItemLayout) {
41 | mOpenedSil.remove(swipeItemLayout);
42 | }
43 |
44 | @Override
45 | public void onBGASwipeItemLayoutStartOpen(BGASwipeItemLayout swipeItemLayout) {
46 | closeOpenedSwipeItemLayoutWithAnim();
47 | }
48 | });
49 | viewHolderHelper.setItemChildClickListener(R.id.tv_item_bgaswipe_delete);
50 | viewHolderHelper.setItemChildLongClickListener(R.id.tv_item_bgaswipe_delete);
51 | }
52 |
53 | @Override
54 | public void fillData(BGAViewHolderHelper viewHolderHelper, int position, NormalModel model) {
55 | viewHolderHelper.setText(R.id.tv_item_bgaswipe_title, model.mTitle).setText(R.id.tv_item_bgaswipe_detail, model.mDetail).setText(R.id.et_item_bgaswipe_title, model.mTitle);
56 |
57 | BGASwipeItemLayout swipeItemLayout = viewHolderHelper.getView(R.id.sil_item_bgaswipe_root);
58 | if (position % 3 == 0) {
59 | swipeItemLayout.setSwipeAble(false);
60 | } else {
61 | swipeItemLayout.setSwipeAble(true);
62 | }
63 | }
64 |
65 | public void closeOpenedSwipeItemLayoutWithAnim() {
66 | for (BGASwipeItemLayout sil : mOpenedSil) {
67 | sil.closeWithAnim();
68 | }
69 | mOpenedSil.clear();
70 | }
71 | }
--------------------------------------------------------------------------------
/demo/src/main/java/cn/bingoogolapple/swipeitemlayout/demo/engine/DataEngine.java:
--------------------------------------------------------------------------------
1 | package cn.bingoogolapple.swipeitemlayout.demo.engine;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import cn.bingoogolapple.swipeitemlayout.demo.model.NormalModel;
7 |
8 | /**
9 | * 作者:王浩 邮件:bingoogolapple@gmail.com
10 | * 创建时间:15/5/26 上午1:03
11 | * 描述:
12 | */
13 | public class DataEngine {
14 |
15 | public static List loadNormalModelDatas() {
16 | List datas = new ArrayList<>();
17 | for (int i = 0; i < 20; i++) {
18 | if (i % 4 == 0) {
19 | datas.add(new NormalModel("标题" + i, "我是短的描述" + i));
20 | } else {
21 | datas.add(new NormalModel("标题" + i, "我是很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的描述" + i));
22 | }
23 | }
24 | return datas;
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/demo/src/main/java/cn/bingoogolapple/swipeitemlayout/demo/model/NormalModel.java:
--------------------------------------------------------------------------------
1 | package cn.bingoogolapple.swipeitemlayout.demo.model;
2 |
3 | /**
4 | * 作者:王浩 邮件:bingoogolapple@gmail.com
5 | * 创建时间:15/5/21 14:53
6 | * 描述:
7 | */
8 | public class NormalModel {
9 | public String mTitle;
10 | public String mDetail;
11 |
12 | public NormalModel(String title, String detail) {
13 | mTitle = title;
14 | mDetail = detail;
15 | }
16 | }
--------------------------------------------------------------------------------
/demo/src/main/java/cn/bingoogolapple/swipeitemlayout/demo/widget/Divider.java:
--------------------------------------------------------------------------------
1 | package cn.bingoogolapple.swipeitemlayout.demo.widget;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Rect;
6 | import android.graphics.drawable.Drawable;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.view.View;
9 |
10 | import cn.bingoogolapple.swipeitemlayout.demo.R;
11 |
12 | public class Divider extends RecyclerView.ItemDecoration {
13 | private Drawable mDividerDrawable;
14 |
15 | public Divider(Context context) {
16 | mDividerDrawable = context.getResources().getDrawable(R.mipmap.list_divider);
17 | }
18 |
19 | // 如果等于分割线的宽度或高度的话可以不用重写该方法
20 | @Override
21 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
22 | if (parent.getChildAdapterPosition(view) == parent.getAdapter().getItemCount() - 1) {
23 | outRect.set(0, 0, 0, 0);
24 | } else {
25 | outRect.set(0, 0, 0, mDividerDrawable.getIntrinsicHeight());
26 | }
27 | }
28 |
29 | @Override
30 | public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
31 | drawVertical(c, parent);
32 | }
33 |
34 | public void drawVertical(Canvas c, RecyclerView parent) {
35 | int left = parent.getPaddingLeft();
36 | int right = parent.getWidth() - parent.getPaddingRight();
37 | View child;
38 | RecyclerView.LayoutParams layoutParams;
39 | int top;
40 | int bottom;
41 | int childCount = parent.getChildCount();
42 | for (int i = 0; i < childCount - 1; i++) {
43 | child = parent.getChildAt(i);
44 | layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
45 | top = child.getBottom() + layoutParams.bottomMargin;
46 | bottom = top + mDividerDrawable.getIntrinsicHeight();
47 | mDividerDrawable.setBounds(left, top, right, bottom);
48 | mDividerDrawable.draw(c);
49 | }
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/selector_btn_orange.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 | -
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/selector_btn_red.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 | -
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/demo/src/main/res/drawable/selector_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 | -
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_listview.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
17 |
18 |
22 |
23 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_recyclerview.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/activity_swipeitem.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
13 |
14 |
18 |
19 |
20 |
21 |
25 |
26 |
29 |
30 |
31 |
32 |
36 |
37 |
41 |
42 |
46 |
47 |
51 |
52 |
56 |
57 |
61 |
62 |
63 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
79 |
80 |
81 |
82 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
97 |
98 |
99 |
100 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
115 |
116 |
117 |
118 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
133 |
134 |
135 |
136 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
151 |
152 |
153 |
154 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
168 |
169 |
170 |
171 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
185 |
186 |
187 |
188 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
202 |
203 |
204 |
205 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
219 |
220 |
221 |
222 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/item_bgaswipe.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
22 |
23 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
53 |
54 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/item_bgaswipe_bak.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
15 |
19 |
20 |
31 |
32 |
33 |
37 |
38 |
44 |
45 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/demo/src/main/res/layout/item_bgaswipe_bak_gap.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
22 |
23 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
53 |
54 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-hdpi/avator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bingoogolapple/BGASwipeItemLayout-Android/31a704345d5b2cc65d9f33e272a9ab01623171da/demo/src/main/res/mipmap-hdpi/avator.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-hdpi/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bingoogolapple/BGASwipeItemLayout-Android/31a704345d5b2cc65d9f33e272a9ab01623171da/demo/src/main/res/mipmap-hdpi/delete.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bingoogolapple/BGASwipeItemLayout-Android/31a704345d5b2cc65d9f33e272a9ab01623171da/demo/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-hdpi/list_divider.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bingoogolapple/BGASwipeItemLayout-Android/31a704345d5b2cc65d9f33e272a9ab01623171da/demo/src/main/res/mipmap-hdpi/list_divider.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bingoogolapple/BGASwipeItemLayout-Android/31a704345d5b2cc65d9f33e272a9ab01623171da/demo/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bingoogolapple/BGASwipeItemLayout-Android/31a704345d5b2cc65d9f33e272a9ab01623171da/demo/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bingoogolapple/BGASwipeItemLayout-Android/31a704345d5b2cc65d9f33e272a9ab01623171da/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #f57257
4 | #f46444
5 | #f46444
6 |
7 | #CCf57257
8 |
9 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BGASwipeItemLayoutDemo
3 |
4 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
18 |
19 |
26 |
27 |
32 |
33 |
42 |
43 |
49 |
50 |
55 |
56 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/demo/src/main/res/values/styles_base.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
13 |
14 |
18 |
19 |
23 |
24 |
29 |
30 |
35 |
36 |
41 |
42 |
47 |
48 |
51 |
52 |
55 |
56 |
59 |
60 |
63 |
64 |
67 |
68 |
71 |
72 |
75 |
76 |
79 |
80 |
83 |
84 |
87 |
88 |
91 |
92 |
95 |
96 |
99 |
100 |
103 |
104 |
107 |
108 |
111 |
112 |
116 |
117 |
121 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ANDROID_BUILD_MIN_SDK_VERSION=10
2 | ANDROID_BUILD_TARGET_SDK_VERSION=25
3 | ANDROID_BUILD_SDK_VERSION=25
4 | ANDROID_BUILD_TOOLS_VERSION=25.0.0
5 |
6 | VERSION_NAME=1.0.4
7 | VERSION_CODE=104
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bingoogolapple/BGASwipeItemLayout-Android/31a704345d5b2cc65d9f33e272a9ab01623171da/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Sep 21 09:01:59 CST 2016
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 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion ANDROID_BUILD_SDK_VERSION as int
5 | buildToolsVersion ANDROID_BUILD_TOOLS_VERSION
6 |
7 | defaultConfig {
8 | minSdkVersion ANDROID_BUILD_MIN_SDK_VERSION as int
9 | targetSdkVersion ANDROID_BUILD_TARGET_SDK_VERSION as int
10 | }
11 | }
12 |
13 | dependencies {
14 | compile 'com.android.support:support-v4:25.0.1'
15 | }
16 |
17 | // gradle uploadArchives
18 | //apply from: 'https://raw.githubusercontent.com/bingoogolapple/PublishAar/master/central-publish.gradle'
--------------------------------------------------------------------------------
/library/gradle.properties:
--------------------------------------------------------------------------------
1 | PUBLISH_AAR_ARTIFACT_ID=bga-swipeitemlayout
2 | PUBLISH_AAR_DESCRIPTION=Android AdapterView And RecyclerView SwipeItemLayout Library
3 | PUBLISH_AAR_GITHUB_REPOSITORIES_NAME=BGASwipeItemLayout-Android
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/library/src/main/java/cn/bingoogolapple/swipeitemlayout/BGASwipeItemLayout.java:
--------------------------------------------------------------------------------
1 | package cn.bingoogolapple.swipeitemlayout;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.os.Bundle;
6 | import android.os.Parcelable;
7 | import android.support.v4.view.GestureDetectorCompat;
8 | import android.support.v4.view.ViewCompat;
9 | import android.support.v4.widget.ViewDragHelper;
10 | import android.util.AttributeSet;
11 | import android.view.GestureDetector;
12 | import android.view.HapticFeedbackConstants;
13 | import android.view.MotionEvent;
14 | import android.view.View;
15 | import android.view.ViewParent;
16 | import android.widget.AbsListView;
17 | import android.widget.AdapterView;
18 | import android.widget.FrameLayout;
19 |
20 | import java.lang.reflect.Method;
21 |
22 | /**
23 | * 作者:王浩 邮件:bingoogolapple@gmail.com
24 | * 创建时间:15/5/26 上午2:07
25 | * 描述:适用于AbsListView和RecyclerView的水平方向滑动item。【作为AbsListView的item单击和长按参考代码家的https://github.com/daimajia/AndroidSwipeLayout】
26 | */
27 | public class BGASwipeItemLayout extends FrameLayout {
28 | private static final String TAG = BGASwipeItemLayout.class.getSimpleName();
29 | private static final String INSTANCE_STATUS = "instance_status";
30 | private static final String STATUS_OPEN_CLOSE = "status_open_close";
31 | private static final int VEL_THRESHOLD = 400;
32 | private ViewDragHelper mDragHelper;
33 | // 顶部视图
34 | private View mTopView;
35 | // 底部视图
36 | private View mBottomView;
37 | // 拖动的弹簧距离
38 | private int mSpringDistance = 0;
39 | // 允许拖动的距离【注意:最终允许拖动的距离是 (mDragRange + mSpringDistance)】
40 | private int mDragRange;
41 | // 控件滑动方向(向左,向右),默认向左滑动
42 | private SwipeDirection mSwipeDirection = SwipeDirection.Left;
43 | // 移动过程中,底部视图的移动方式(拉出,被顶部视图遮住),默认是被顶部视图遮住
44 | private BottomModel mBottomModel = BottomModel.PullOut;
45 | // 滑动控件当前的状态(打开,关闭,正在移动),默认是关闭状态
46 | private Status mCurrentStatus = Status.Closed;
47 | // 滑动控件滑动前的状态
48 | private Status mPreStatus = mCurrentStatus;
49 | // 顶部视图下一次layout时的left
50 | private int mTopLeft;
51 | // 顶部视图外边距
52 | private MarginLayoutParams mTopLp;
53 | // 底部视图外边距
54 | private MarginLayoutParams mBottomLp;
55 | // 滑动比例,【关闭->展开 => 0->1】
56 | private float mDragRatio;
57 | // 手动拖动打开和关闭代理
58 | private BGASwipeItemLayoutDelegate mDelegate;
59 |
60 | private GestureDetectorCompat mGestureDetectorCompat;
61 | private OnLongClickListener mOnLongClickListener;
62 | private OnClickListener mOnClickListener;
63 | /**
64 | * 是否可滑动
65 | */
66 | private boolean mSwipeable = true;
67 |
68 | public BGASwipeItemLayout(Context context, AttributeSet attrs) {
69 | this(context, attrs, 0);
70 | }
71 |
72 | public BGASwipeItemLayout(Context context, AttributeSet attrs, int defStyleAttr) {
73 | super(context, attrs, defStyleAttr);
74 | initAttrs(context, attrs);
75 | initProperty();
76 | }
77 |
78 | private void initAttrs(Context context, AttributeSet attrs) {
79 | final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BGASwipeItemLayout);
80 | final int N = typedArray.getIndexCount();
81 | for (int i = 0; i < N; i++) {
82 | initAttr(typedArray.getIndex(i), typedArray);
83 | }
84 | typedArray.recycle();
85 | }
86 |
87 | private void initAttr(int attr, TypedArray typedArray) {
88 | if (attr == R.styleable.BGASwipeItemLayout_bga_sil_swipeDirection) {
89 | // 默认向左滑动
90 | int leftSwipeDirection = typedArray.getInt(attr, mSwipeDirection.ordinal());
91 |
92 | if (leftSwipeDirection == SwipeDirection.Right.ordinal()) {
93 | mSwipeDirection = SwipeDirection.Right;
94 | }
95 | } else if (attr == R.styleable.BGASwipeItemLayout_bga_sil_bottomMode) {
96 | // 默认是拉出
97 | int pullOutBottomMode = typedArray.getInt(attr, mBottomModel.ordinal());
98 |
99 | if (pullOutBottomMode == BottomModel.LayDown.ordinal()) {
100 | mBottomModel = BottomModel.LayDown;
101 | }
102 | } else if (attr == R.styleable.BGASwipeItemLayout_bga_sil_springDistance) {
103 | // 弹簧距离,不能小于0,默认值为0
104 | mSpringDistance = typedArray.getDimensionPixelSize(attr, mSpringDistance);
105 | if (mSpringDistance < 0) {
106 | throw new IllegalStateException("bga_sil_springDistance不能小于0");
107 | }
108 | } else if (attr == R.styleable.BGASwipeItemLayout_bga_sil_swipeAble) {
109 | mSwipeable = typedArray.getBoolean(attr, mSwipeable);
110 | }
111 | }
112 |
113 | private void initProperty() {
114 | mDragHelper = ViewDragHelper.create(this, mDragHelperCallback);
115 | mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
116 | mGestureDetectorCompat = new GestureDetectorCompat(getContext(), mSimpleOnGestureListener);
117 | }
118 |
119 | public void setDelegate(BGASwipeItemLayoutDelegate delegate) {
120 | mDelegate = delegate;
121 | }
122 |
123 | public void setSwipeAble(boolean swipeAble) {
124 | mSwipeable = swipeAble;
125 | }
126 |
127 | @Override
128 | protected void onFinishInflate() {
129 | super.onFinishInflate();
130 | if (getChildCount() != 2) {
131 | throw new IllegalStateException(BGASwipeItemLayout.class.getSimpleName() + "必须有且只有两个子控件");
132 | }
133 | mTopView = getChildAt(1);
134 | mBottomView = getChildAt(0);
135 | // 避免底部视图被隐藏时还能获取焦点被点击
136 | mBottomView.setVisibility(INVISIBLE);
137 |
138 | mTopLp = (MarginLayoutParams) mTopView.getLayoutParams();
139 | mBottomLp = (MarginLayoutParams) mBottomView.getLayoutParams();
140 | mTopLeft = getPaddingLeft() + mTopLp.leftMargin;
141 | }
142 |
143 | @Override
144 | public void computeScroll() {
145 | if (mDragHelper.continueSettling(true)) {
146 | ViewCompat.postInvalidateOnAnimation(this);
147 | }
148 | }
149 |
150 | @Override
151 | public boolean onInterceptTouchEvent(MotionEvent ev) {
152 | if (ev.getAction() == MotionEvent.ACTION_CANCEL || ev.getAction() == MotionEvent.ACTION_UP) {
153 | mDragHelper.cancel();
154 | }
155 | return mDragHelper.shouldInterceptTouchEvent(ev) && mGestureDetectorCompat.onTouchEvent(ev);
156 | }
157 |
158 | @Override
159 | public void setOnClickListener(OnClickListener l) {
160 | super.setOnClickListener(l);
161 | mOnClickListener = l;
162 | }
163 |
164 | @Override
165 | public void setOnLongClickListener(OnLongClickListener l) {
166 | super.setOnLongClickListener(l);
167 | mOnLongClickListener = l;
168 | }
169 |
170 | @Override
171 | protected void onAttachedToWindow() {
172 | super.onAttachedToWindow();
173 |
174 | if (insideAdapterView()) {
175 | if (mOnClickListener == null) {
176 | setOnClickListener(new OnClickListener() {
177 | @Override
178 | public void onClick(View v) {
179 | performAdapterViewItemClick();
180 | }
181 | });
182 | }
183 | if (mOnLongClickListener == null) {
184 | setOnLongClickListener(new OnLongClickListener() {
185 | @Override
186 | public boolean onLongClick(View v) {
187 | performAdapterViewItemLongClick();
188 | return true;
189 | }
190 | });
191 | }
192 | }
193 | }
194 |
195 | private void performAdapterViewItemClick() {
196 | ViewParent t = getParent();
197 | if (t instanceof AdapterView) {
198 | AdapterView view = (AdapterView) t;
199 | int p = view.getPositionForView(BGASwipeItemLayout.this);
200 | if (p != AdapterView.INVALID_POSITION) {
201 | view.performItemClick(view.getChildAt(p - view.getFirstVisiblePosition()), p, view.getAdapter().getItemId(p));
202 | }
203 | }
204 | }
205 |
206 | private boolean performAdapterViewItemLongClick() {
207 | ViewParent t = getParent();
208 | if (t instanceof AdapterView) {
209 | AdapterView view = (AdapterView) t;
210 | int p = view.getPositionForView(BGASwipeItemLayout.this);
211 | if (p == AdapterView.INVALID_POSITION) return false;
212 | long vId = view.getItemIdAtPosition(p);
213 | boolean handled = false;
214 | try {
215 | Method m = AbsListView.class.getDeclaredMethod("performLongPress", View.class, int.class, long.class);
216 | m.setAccessible(true);
217 | handled = (boolean) m.invoke(view, BGASwipeItemLayout.this, p, vId);
218 |
219 | } catch (Exception e) {
220 | e.printStackTrace();
221 |
222 | if (view.getOnItemLongClickListener() != null) {
223 | handled = view.getOnItemLongClickListener().onItemLongClick(view, BGASwipeItemLayout.this, p, vId);
224 | }
225 | if (handled) {
226 | view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
227 | }
228 | }
229 | return handled;
230 | }
231 | return false;
232 | }
233 |
234 | private boolean insideAdapterView() {
235 | return getAdapterView() != null;
236 | }
237 |
238 | private AdapterView getAdapterView() {
239 | ViewParent t = getParent();
240 | if (t instanceof AdapterView) {
241 | return (AdapterView) t;
242 | }
243 | return null;
244 | }
245 |
246 | private void requestParentDisallowInterceptTouchEvent() {
247 | ViewParent parent = getParent();
248 | if (parent != null) {
249 | parent.requestDisallowInterceptTouchEvent(true);
250 | }
251 | }
252 |
253 | private GestureDetector.SimpleOnGestureListener mSimpleOnGestureListener = new GestureDetector.SimpleOnGestureListener() {
254 | @Override
255 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
256 | if (Math.abs(distanceX) > Math.abs(distanceY)) {
257 | requestParentDisallowInterceptTouchEvent();
258 | return true;
259 | }
260 | return false;
261 | }
262 |
263 | @Override
264 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
265 | if (Math.abs(velocityX) > Math.abs(velocityY)) {
266 | requestParentDisallowInterceptTouchEvent();
267 | return true;
268 | }
269 | return false;
270 | }
271 |
272 | @Override
273 | public boolean onSingleTapConfirmed(MotionEvent e) {
274 | // 2
275 | setPressed(false);
276 | if (isClosed()) {
277 | return performClick();
278 | }
279 | return false;
280 | }
281 |
282 | @Override
283 | public boolean onSingleTapUp(MotionEvent e) {
284 | // 1
285 | if (isClosed()) {
286 | setPressed(true);
287 | return true;
288 | }
289 | return false;
290 | }
291 |
292 | @Override
293 | public void onLongPress(MotionEvent e) {
294 | if (isClosed()) {
295 | setPressed(true);
296 | postDelayed(mCancelPressedTask, 300);
297 | performLongClick();
298 | }
299 | }
300 |
301 | // 作为ListView或者RecyclerView的item,双击事件很少,这里就不处理双击事件了╮(╯_╰)╭
302 | public boolean onDoubleTap(MotionEvent e) {
303 | if (isClosed()) {
304 | setPressed(true);
305 | return true;
306 | }
307 | return false;
308 | }
309 |
310 | public boolean onDoubleTapEvent(MotionEvent e) {
311 | if (isClosed()) {
312 | setPressed(false);
313 | return true;
314 | }
315 | return false;
316 | }
317 | };
318 |
319 | private Runnable mCancelPressedTask = new Runnable() {
320 | @Override
321 | public void run() {
322 | setPressed(false);
323 | }
324 | };
325 |
326 | @Override
327 | public boolean onTouchEvent(MotionEvent event) {
328 | mDragHelper.processTouchEvent(event);
329 | mGestureDetectorCompat.onTouchEvent(event);
330 | return true;
331 | }
332 |
333 | @Override
334 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
335 | mDragRange = mBottomView.getMeasuredWidth() + mBottomLp.leftMargin + mBottomLp.rightMargin;
336 |
337 | int topTop = getPaddingTop() + mTopLp.topMargin;
338 | int topBottom = getMeasuredHeight() - getPaddingBottom() - mTopLp.bottomMargin;
339 | int topRight = mTopLeft + mTopView.getMeasuredWidth();
340 |
341 | int bottomTop = getPaddingTop() + mBottomLp.topMargin;
342 | int bottomBottom = getMeasuredHeight() - getPaddingBottom() - mBottomLp.bottomMargin;
343 | int bottomLeft;
344 | int bottomRight;
345 |
346 | if (mSwipeDirection == SwipeDirection.Left) {
347 | // 向左滑动
348 |
349 | if (mBottomModel == BottomModel.LayDown) {
350 | // 遮罩,位置固定不变(先计算right,然后根据right计算left)
351 |
352 | bottomRight = getMeasuredWidth() - getPaddingRight() - mBottomLp.rightMargin;
353 | bottomLeft = bottomRight - mBottomView.getMeasuredWidth();
354 | } else {
355 | // 拉出,位置随顶部视图的位置改变
356 |
357 | // 根据顶部视图的left计算底部视图的left
358 | bottomLeft = mTopLeft + mTopView.getMeasuredWidth() + mTopLp.rightMargin + mBottomLp.leftMargin;
359 |
360 | // 底部视图的left被允许的最小值
361 | int minBottomLeft = getMeasuredWidth() - getPaddingRight() - mBottomView.getMeasuredWidth() - mBottomLp.rightMargin;
362 | // 获取最终的left
363 | bottomLeft = Math.max(bottomLeft, minBottomLeft);
364 | // 根据left计算right
365 | bottomRight = bottomLeft + mBottomView.getMeasuredWidth();
366 | }
367 | } else {
368 | // 向右滑动
369 |
370 | if (mBottomModel == BottomModel.LayDown) {
371 | // 遮罩,位置固定不变(先计算left,然后根据left计算right)
372 |
373 | bottomLeft = getPaddingLeft() + mBottomLp.leftMargin;
374 | bottomRight = bottomLeft + mBottomView.getMeasuredWidth();
375 | } else {
376 | // 拉出,位置随顶部视图的位置改变
377 |
378 | // 根据顶部视图的left计算底部视图的left
379 | bottomLeft = mTopLeft - mDragRange;
380 | // 底部视图的left被允许的最大值
381 | int maxBottomLeft = getPaddingLeft() + mBottomLp.leftMargin;
382 | // 获取最终的left
383 | bottomLeft = Math.min(maxBottomLeft, bottomLeft);
384 | // 根据left计算right
385 | bottomRight = bottomLeft + mBottomView.getMeasuredWidth();
386 | }
387 | }
388 |
389 | mBottomView.layout(bottomLeft, bottomTop, bottomRight, bottomBottom);
390 | mTopView.layout(mTopLeft, topTop, topRight, topBottom);
391 | }
392 |
393 | /**
394 | * 以动画方式打开
395 | */
396 | public void openWithAnim() {
397 | mPreStatus = Status.Moving;
398 | smoothSlideTo(1);
399 | }
400 |
401 | /**
402 | * 以动画方式关闭
403 | */
404 | public void closeWithAnim() {
405 | mPreStatus = Status.Moving;
406 | smoothSlideTo(0);
407 | }
408 |
409 | /**
410 | * 直接打开
411 | */
412 | public void open() {
413 | mPreStatus = Status.Moving;
414 | slideTo(1);
415 | }
416 |
417 | /**
418 | * 直接关闭。如果在AbsListView中删除已经打开的item时,请用该方法关闭item,否则重用item时有问题。RecyclerView中可以用该方法,也可以用closeWithAnim
419 | */
420 | public void close() {
421 | mPreStatus = Status.Moving;
422 | slideTo(0);
423 | }
424 |
425 | /**
426 | * 当前是否为打开状态
427 | *
428 | * @return
429 | */
430 | public boolean isOpened() {
431 | return (mCurrentStatus == Status.Opened) || (mCurrentStatus == Status.Moving && mPreStatus == Status.Opened);
432 | }
433 |
434 | /**
435 | * 当前是否为关闭状态
436 | *
437 | * @return
438 | */
439 | public boolean isClosed() {
440 | return mCurrentStatus == Status.Closed || (mCurrentStatus == Status.Moving && mPreStatus == Status.Closed);
441 | }
442 |
443 | /**
444 | * 获取顶部视图
445 | *
446 | * @return
447 | */
448 | public View getTopView() {
449 | return mTopView;
450 | }
451 |
452 | /**
453 | * 获取底部视图
454 | *
455 | * @return
456 | */
457 | public View getBottomView() {
458 | return mBottomView;
459 | }
460 |
461 | /**
462 | * 打开或关闭滑动控件
463 | *
464 | * @param isOpen 1表示打开,0表示关闭
465 | */
466 | private void smoothSlideTo(int isOpen) {
467 | if (mDragHelper.smoothSlideViewTo(mTopView, getCloseOrOpenTopViewFinalLeft(isOpen), getPaddingTop() + mTopLp.topMargin)) {
468 | ViewCompat.postInvalidateOnAnimation(this);
469 | }
470 | }
471 |
472 | /**
473 | * 打开或关闭滑动控件
474 | *
475 | * @param isOpen 1表示打开,0表示关闭
476 | */
477 | private void slideTo(int isOpen) {
478 | if (isOpen == 1) {
479 | mBottomView.setVisibility(VISIBLE);
480 | ViewCompat.setAlpha(mBottomView, 1.0f);
481 | mCurrentStatus = Status.Opened;
482 | if (mDelegate != null) {
483 | mDelegate.onBGASwipeItemLayoutOpened(this);
484 | }
485 | } else {
486 | mBottomView.setVisibility(INVISIBLE);
487 | mCurrentStatus = Status.Closed;
488 | if (mDelegate != null) {
489 | mDelegate.onBGASwipeItemLayoutClosed(this);
490 | }
491 | }
492 | mPreStatus = mCurrentStatus;
493 | mTopLeft = getCloseOrOpenTopViewFinalLeft(isOpen);
494 | requestLayout();
495 | }
496 |
497 | private int getCloseOrOpenTopViewFinalLeft(int isOpen) {
498 | int left = getPaddingLeft() + mTopLp.leftMargin;
499 | if (mSwipeDirection == SwipeDirection.Left) {
500 | left = left - isOpen * mDragRange;
501 | } else {
502 | left = left + isOpen * mDragRange;
503 | }
504 | return left;
505 | }
506 |
507 | @Override
508 | public Parcelable onSaveInstanceState() {
509 | Bundle bundle = new Bundle();
510 | bundle.putParcelable(INSTANCE_STATUS, super.onSaveInstanceState());
511 | bundle.putInt(STATUS_OPEN_CLOSE, mCurrentStatus.ordinal());
512 | return bundle;
513 | }
514 |
515 | @Override
516 | public void onRestoreInstanceState(Parcelable state) {
517 | if (state instanceof Bundle) {
518 | Bundle bundle = (Bundle) state;
519 | if (bundle.getInt(STATUS_OPEN_CLOSE) == Status.Opened.ordinal()) {
520 | open();
521 | } else {
522 | close();
523 | }
524 | super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATUS));
525 | } else {
526 | super.onRestoreInstanceState(state);
527 | }
528 | }
529 |
530 | private ViewDragHelper.Callback mDragHelperCallback = new ViewDragHelper.Callback() {
531 |
532 | @Override
533 | public boolean tryCaptureView(View child, int pointerId) {
534 | return mSwipeable && child == mTopView;
535 | }
536 |
537 | @Override
538 | public int getViewVerticalDragRange(View child) {
539 | return 0;
540 | }
541 |
542 | @Override
543 | public int clampViewPositionVertical(View child, int top, int dy) {
544 | // 这里要返回控件的getPaddingTop() + mTopLp.topMargin,否则有margin和padding快速滑动松手时会上下跳动
545 | return getPaddingTop() + mTopLp.topMargin;
546 | }
547 |
548 | @Override
549 | public int getViewHorizontalDragRange(View child) {
550 | return mDragRange + mSpringDistance;
551 | }
552 |
553 | /**
554 | *
555 | * @param child
556 | * @param left ViewDragHelper帮我们计算的当前所捕获的控件的left
557 | * @param dx
558 | * @return
559 | */
560 | @Override
561 | public int clampViewPositionHorizontal(View child, int left, int dx) {
562 | int minTopLeft;
563 | int maxTopLeft;
564 |
565 | if (mSwipeDirection == SwipeDirection.Left) {
566 | // 向左滑动
567 |
568 | // 顶部视图的left被允许的最小值
569 | minTopLeft = getPaddingLeft() + mTopLp.leftMargin - (mDragRange + mSpringDistance);
570 | // 顶部视图的left被允许的最大值
571 | maxTopLeft = getPaddingLeft() + mTopLp.leftMargin;
572 | } else {
573 | // 向右滑动
574 |
575 | // 顶部视图的left被允许的最小值
576 | minTopLeft = getPaddingLeft() + mTopLp.leftMargin;
577 | // 顶部视图的left被允许的最大值
578 | maxTopLeft = getPaddingLeft() + mTopLp.leftMargin + (mDragRange + mSpringDistance);
579 | }
580 |
581 | return Math.min(Math.max(minTopLeft, left), maxTopLeft);
582 | }
583 |
584 | @Override
585 | public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
586 | mTopLeft = left;
587 |
588 | // 此时顶部视图水平方向偏移量的绝对值
589 | int topViewHorizontalOffset = Math.abs(mTopLeft - (getPaddingLeft() + mTopLp.leftMargin));
590 | if (topViewHorizontalOffset > mDragRange) {
591 | mDragRatio = 1.0f;
592 | } else {
593 | mDragRatio = 1.0f * topViewHorizontalOffset / mDragRange;
594 | }
595 |
596 | // 处理底部视图的透明度
597 | float alpha = 0.1f + 0.9f * mDragRatio;
598 | ViewCompat.setAlpha(mBottomView, alpha);
599 |
600 | dispatchSwipeEvent();
601 |
602 | // 兼容低版本
603 | invalidate();
604 |
605 | requestLayout();
606 | }
607 |
608 | @Override
609 | public void onViewReleased(View releasedChild, float xvel, float yvel) {
610 | // 默认关闭,接下来再判断为打开时的条件
611 | int finalLeft = getPaddingLeft() + mTopLp.leftMargin;
612 | if (mSwipeDirection == SwipeDirection.Left) {
613 | // 向左滑动为打开,向右滑动为关闭
614 |
615 | if (xvel < -VEL_THRESHOLD || (mPreStatus == Status.Closed && xvel < VEL_THRESHOLD && mDragRatio >= 0.3f) || (mPreStatus == Status.Opened && xvel < VEL_THRESHOLD && mDragRatio >= 0.7f)) {
616 | // 向左的速度达到条件
617 | finalLeft -= mDragRange;
618 | }
619 |
620 | } else {
621 | // 向左滑动为关闭,向右滑动为打开
622 |
623 | if (xvel > VEL_THRESHOLD || (mPreStatus == Status.Closed && xvel > -VEL_THRESHOLD && mDragRatio >= 0.3f) || (mPreStatus == Status.Opened && xvel > -VEL_THRESHOLD && mDragRatio >= 0.7f)) {
624 | finalLeft += mDragRange;
625 | }
626 | }
627 | mDragHelper.settleCapturedViewAt(finalLeft, getPaddingTop() + mTopLp.topMargin);
628 |
629 | // 要执行下面的代码,不然不会自动收缩完毕或展开完毕
630 | ViewCompat.postInvalidateOnAnimation(BGASwipeItemLayout.this);
631 | }
632 |
633 | };
634 |
635 | private void dispatchSwipeEvent() {
636 | Status preStatus = mCurrentStatus;
637 | updateCurrentStatus();
638 | if (mCurrentStatus != preStatus) {
639 | if (mCurrentStatus == Status.Closed) {
640 | mBottomView.setVisibility(INVISIBLE);
641 | if (mDelegate != null && mPreStatus != mCurrentStatus) {
642 | mDelegate.onBGASwipeItemLayoutClosed(this);
643 | }
644 | mPreStatus = Status.Closed;
645 | } else if (mCurrentStatus == Status.Opened) {
646 | if (mDelegate != null && mPreStatus != mCurrentStatus) {
647 | mDelegate.onBGASwipeItemLayoutOpened(this);
648 | }
649 | mPreStatus = Status.Opened;
650 | } else if (mPreStatus == Status.Closed) {
651 | mBottomView.setVisibility(VISIBLE);
652 | if (mDelegate != null) {
653 | mDelegate.onBGASwipeItemLayoutStartOpen(this);
654 | }
655 | }
656 | }
657 | }
658 |
659 | private void updateCurrentStatus() {
660 | if (mSwipeDirection == SwipeDirection.Left) {
661 | // 向左滑动
662 |
663 | if (mTopLeft == getPaddingLeft() + mTopLp.leftMargin - mDragRange) {
664 | mCurrentStatus = Status.Opened;
665 | } else if (mTopLeft == getPaddingLeft() + mTopLp.leftMargin) {
666 | mCurrentStatus = Status.Closed;
667 | } else {
668 | mCurrentStatus = Status.Moving;
669 | }
670 | } else {
671 | // 向右滑动
672 |
673 | if (mTopLeft == getPaddingLeft() + mTopLp.leftMargin + mDragRange) {
674 | mCurrentStatus = Status.Opened;
675 | } else if (mTopLeft == getPaddingLeft() + mTopLp.leftMargin) {
676 | mCurrentStatus = Status.Closed;
677 | } else {
678 | mCurrentStatus = Status.Moving;
679 | }
680 | }
681 | }
682 |
683 |
684 | public enum SwipeDirection {
685 | Left, Right
686 | }
687 |
688 | public enum BottomModel {
689 | PullOut, LayDown
690 | }
691 |
692 | public enum Status {
693 | Opened, Closed, Moving
694 | }
695 |
696 | public interface BGASwipeItemLayoutDelegate {
697 | /**
698 | * 变为打开状态
699 | *
700 | * @param swipeItemLayout
701 | */
702 | void onBGASwipeItemLayoutOpened(BGASwipeItemLayout swipeItemLayout);
703 |
704 | /**
705 | * 变为关闭状态
706 | *
707 | * @param swipeItemLayout
708 | */
709 | void onBGASwipeItemLayoutClosed(BGASwipeItemLayout swipeItemLayout);
710 |
711 | /**
712 | * 从关闭状态切换到正在打开状态
713 | *
714 | * @param swipeItemLayout
715 | */
716 | void onBGASwipeItemLayoutStartOpen(BGASwipeItemLayout swipeItemLayout);
717 | }
718 |
719 | }
--------------------------------------------------------------------------------
/library/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':library', ':demo'
--------------------------------------------------------------------------------