This control does not depend on any parent layout, not for
Recycler View, ListView, and child View in any ViewGroup can use the Side Slide (Delete) menu.
Simple, 0-coupled, supports any ViewGroup.
Summary
It has been 7 months since the control came out and has been used in the project. It has been 2 months since the first push to github. I have published an article before. Portal: http://blog.csdn.net/zxt0601/article/details/52303781 It describes in detail how the V1.0 version of this control is implemented.)
During this period, many friends put forward some improvements in their comments and issue s, such as support for setting sliding direction (left and right), interaction with high imitation QQ, support for GridLayout Manager, and some bug s. It has been fully restored by me. And it is packaged into jitpack, which is more convenient to introduce. Compared with the first edition, there are many changes. Therefore, it will be sorted out, a new edition.
So this article starts with how to use it, and then introduces its features and supporting attributes. Finally, several difficulties and conflict resolution are explained.
Code Portal: If you like, just click star. Thank you.
https://github.com/mcxtzhang/SwipeDelMenuLayout
Let's start with a couple of gif s to show you the magic of the latest edition (the following editions show optional two-way sliding in passing)
The greatest charm of this control is 0 coupling, so first with the effect of my other library assembly (ItemDecoration IndexBar + SwipeMenu Layout):
(ItemDecorationIndexBar : https://github.com/mcxtzhang/ItemDecorationIndexBar)
Android Special Version (no blocking, when the sideslip menu expands, other sideslip menus can still be expanded, while the previous menu will automatically close):
GridLayout Manager (just modify the Layout Manager of RecyclerView, as compared to the code shown above.) :
Linear Layout (without any modification, even Linear Layout can simply implement the sideslip menu):
iOS interaction (blocking interaction, high imitation QQ, sideslip menu expansion, shielding all other ITEM operations):
Use:
Step 1. Add JitPack repository dependencies to the project root build.gradle file.
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
- 1
- 2
- 3
- 4
- 5
- 6
Step 2. Add the dependency
dependencies {
compile 'com.github.mcxtzhang:SwipeDelMenuLayout:V1.2.1'
}
- 1
- 2
- 3
Step 3. Cover the Control with the ContentItem that needs to be deleted by side sliding. Arrange the ContentItem and menu in turn within the Control.
At this point, you can use high imitation IOS, QQ sideslip deletion menu function.
(Click events on the sideslip menu are retrieved by setting the id, which is the same as other controls, no more details)
In Demo, my ContentItem is a TextView, so I nest this control outside it, and arrange the menu control in the order that the sideslip menu appears.
<?xml version="1.0" encoding="utf-8"?>
<com.mcxtzhang.swipemenulib.SwipeMenuLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dp"
android:clickable="true"
android:paddingBottom="1dp">
<TextView
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground"
android:gravity="center"
android:text="I am arbitrarily complex in the project. ContentItem layout"/>
<!-- Following are the contents of the sideslip menu arranged in order -->
<Button
android:id="@+id/btnTop"
android:layout_width="60dp"
android:layout_height="match_parent"
android:background="#d9dee4"
android:text="Roof placement"
android:textColor="@android:color/white"/>
<Button
android:id="@+id/btnUnRead"
android:layout_width="120dp"
android:layout_height="match_parent"
android:background="#ecd50a"
android:clickable="true"
android:text="Unread mark"
android:textColor="@android:color/white"/>
<Button
android:id="@+id/btnDelete"
android:layout_width="60dp"
android:layout_height="match_parent"
android:background="@color/red_ff4a57"
android:text="delete"
android:textColor="@android:color/white"/>
</com.mcxtzhang.swipemenulib.SwipeMenuLayout>
- 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
Supporting attributes:
The isIos variable controls whether or not the IOS blocking interaction is open by default.
The isSwipeEnable variable controls whether to open the right-sliding menu or not, and opens by default. (In some scenarios, reuse item s, users without editing rights can't right-click)
3 Support left slip and right slip by isLeft Swipe switch
There are two ways to set up:
1: xml:
<com.mcxtzhang.swipemenulib.SwipeMenuLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
app:ios="false"
app:leftSwipe="true"
app:swipeEnable="true">
- 1
- 2
- 3
- 4
- 5
2: java code:
//Turn off the IOS blocking interaction effect and turn on the left sliding right sliding to disable the side sliding menu in turn.
((SwipeMenuLayout) holder.itemView).setIos(false).setLeftSwipe(position % 2 == 0 ? true : false).setSwipeEnable(false);
- 1
- 2
Support features:
- It does not expand 2 + sideslip menus at the same time. You can see that there is only one sideslip menu on the interface at most.
- In the process of sideslip, the parent control is prohibited from sliding up and down.
- Multiple fingers slide at the same time and touch several fingers after shielding.
- Adding the get() method of viewChache can be used to close the expanding sideslip menu when clicking on the outer blank.
- Take the width of the first sub-Item (that is, ContentItem) as the control width
checklist for each update:
Because of continuous iteration, new bugs will occur after completing a feature and fix ing a bug.
so, organize a checkList for verification after each iteration, and then push it to the github library.
project | Remarks | Verification |
---|---|---|
isIos | Switching to IOS blocking interaction mode and Android feature non-blocking interaction mode, the following features can work properly | |
isSwipeEnable | Whether to Support Closing Side Slip Function | |
isLeftSwipe | Whether to support bi-directional sliding | |
ContentItem content can be clicked | ||
ContentItem content can be long pressed | ||
ContentItem is not clickable when the sideslip menu is displayed | ||
ContentItem is not long to press when the sideslip menu is displayed | ||
When the sideslip menu is displayed, the sideslip menu can be clicked | ||
When the sideslip menu is displayed, click the ContentItem area to close the menu | ||
Shielding long press event during sideslip | ||
The ContentItem click event should not be triggered by sliding the close menu |
Difficulties and conflict resolution:
The long press of 1 ContentItem conflicts with the sideslip of this control.
At first, I was still the old idea, always by judging the coordinates of the initial landing point of the finger, judging where the landing point of the finger is, shielding some operations. When the function of this control becomes more and more huge, the calculation becomes complex and error-prone. But I stumbled through it, but today I came up with another idea: to do this by disabling the long Clickable attribute of the child View. So I refactored this part of the code, first checked the previous submission with git, removed that part of the code, and
In the function of expanding smoothExpand() on the side slide menu and closing smoothClose(), add:
//2016 11 13 add sideslip menu expand, shield content long press
if (null != mContentView) {
mContentView.setLongClickable(true);
}
- 1
- 2
- 3
- 4
- 5
//2016 11 13 add sideslip menu expand, shield content long press
if (null != mContentView) {
mContentView.setLongClickable(true);
}
- 1
- 2
- 3
- 4
The code is so simple, I just thought of such a simple solution a few hours ago, which is also one of the reasons that prompted me to write a new article and record some changes in the control of this control.
2 How to support any parent control
This is one of the cool charms of this control. Previous articles I describe the implementation process in detail.
To sum up, I use a static variable to save the View that is currently unfolding.
It can be pre-judged when performing various operations, such as two side-sliding menus on the interface, which may cause conflicts.
The last menu is automatically closed.
The code is as follows:
//Storing is the currently expanding View
private static SwipeMenuLayout mViewCache;
- 1
- 2
- 3
ActionDown of dispatchTouchEvent():
//If down, view and cache view are different, let it be restored immediately. And set it to null
if (mViewCache != null) {
if (mViewCache != this) {
mViewCache.smoothClose();
}
//As long as one of the sideslip menus is open, it will not slide up and down the outer layout.
getParent().requestDisallowInterceptTouchEvent(true);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
Expand smoothExpand() on the sideslip menu, and close the smoothClose() function:
//Start by adding ViewCache:
mViewCache = SwipeMenuLayout.this;
- 1
- 2
//Close to null
mViewCache = null;
- 1
- 2
In onDetachedFromWindow():
//Each time you view Detach, determine if the ViewCache is itself. If it is yourself, close the sideslip menu and set the ViewCache to null.
// Reason: 1. Prevent memory leaks (ViewCache is a static variable)
// After deleting itself, the View is reclaimed and reused by Recycler. The next View entering the screen should be in the normal state, not the expanded state.
@Override
protected void onDetachedFromWindow() {
if (this == mViewCache) {
mViewCache.smoothClose();
mViewCache = null;
}
super.onDetachedFromWindow();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
3. Resolve multi-finger sliding conflict:
A Boolean flag is used to determine whether to continue accepting touch events during ActionDown:
The code is as follows:
//Prevent multiple fingers from sliding together. My flag judges in every down load. The touch event ends and clears.
private static boolean isTouching;
- 1
- 2
ActionDown of dispatchTouchEvent():
if (isTouching) {//If other fingers have touched it, thenreturn false. So follow-up move..I won't look for this again until something happens.ViewNow.
return false;
} else {
isTouching = true;//The first finger to touch, quickly change the logo, swear sovereignty.
}
- 1
- 2
- 3
- 4
- 5
ActionUp:
isTouching = false;//No fingers are touching me.
- 1
4 Support GridLayout Manager
After all, the use of side-sliding menus in the grid layout is still a minority in the project, so at first I simplified the scene, setting the width of this control is the width of the parent control - padding. Later, some children's shoes proposed to support grid layout. At first, I took a detour. I also wanted to build a MatchParent's MesureSpec and pass it to the parent control (GridView, Recycler View) for measurement.
The correct idea is to use the width of ContentView as the width of this control, which is the first sub-View, so that when layout side-sliding menu, the side-sliding menu is naturally placed in the invisible area and can be displayed only by sliding.
There's nothing to say about the code:
In onMeasure():
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//2016 1109 add, adapted to GridLayout Manager, will have the width of the first sub-Item (that is, ContentItem) as the control width
int contentWidth = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
if (childView.getVisibility() != GONE) {
measureChildWithMargins(childView, widthMeasureSpec, 0, heightMeasureSpec, 0);
final MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
mHeight = Math.max(mHeight, childView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
if (i > 0) {//The first layout is Left item, and the second is RightMenu.
mRightMenuWidths += childView.getMeasuredWidth();
} else {
contentWidth = childView.getMeasuredWidth();
}
}
}
setMeasuredDimension(contentWidth, mHeight);//Take the width of the first Item(Content)
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
In onLayout(), the order layoutchildView is sufficient.