Expandable ListView Side Slide Delete
Recently, there is a need for work, similar to the QQ folding effect, of course, this control is Expandable ListView first thought of, but in the process of use, users put forward new requirements, need to add the side-sliding deletion function, what to do? I also looked at some other processing schemes on the Internet, but the effect is not ideal, and finally with the help of my colleagues through HorizontalScrollView implementation
1. There are two itme layout files for ExpandableListView, group item and subitem. Here we use subitem to sideslide. First we look at the layout file
<?xml version="1.0" encoding="utf-8"?>
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scrollView_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
android:overScrollMode="never"
android:scrollbars="none">
<LinearLayout
android:id="@+id/ll_group_control"
android:layout_width="wrap_content"
android:layout_height="@dimen/activity_item_margin"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/activity_item_margin"
android:background="@mipmap/background_device_one"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_linajie"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/margin_size_10"
android:src="@mipmap/lianjie"/>
<TextView
android:id="@+id/child_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:layout_toRightOf="@id/iv_linajie"
android:text="This is a child item"
android:textColor="@color/write"
android:textSize="@dimen/margin_size_20"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/img_delete"
android:layout_width="70dp"
android:layout_height="match_parent"
android:background="@mipmap/delete_diban">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@mipmap/jianhao"/>
</RelativeLayout>
</LinearLayout>
</HorizontalScrollView>
2. First we define several interfaces
public interface OnAddDeviceListener {
//Click Event
void OnChildClick(int groupPosition, int childPosition);
//Side Slide Delete Event
void OnChildDel(int groupPosition, int childPosition);
//Long press event
void OnChildLongClick(int groupPosition, int childPosition);
}
public void setOnAddDeviceListener(OnAddDeviceListener l) {
this.listener = l;
}
private OnAddDeviceListener listener;
There must be a lot of questions here. Defining a side-slip callback is reasonably enough. Why bother with defining clicks and long-click events? One of the questions here is that when dealing with a side-slip event through HorizontalScrollView, it will duplicate its onTouch event, which will invalidate the click event of the control itself, so here we need to define this by ourselvesSeveral events
- The adapter handles, omits other code, and directly handles getChildView methods related to subclasses, as follows
// Get the view displayed by the subitem
@Override
public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View view, ViewGroup parent) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.child_item, null);
}
view.setTag(R.layout.parent_item, groupPosition);
view.setTag(R.layout.child_item, childPosition);
//Initialize the HorizontalScrollView control here and set the TAG tag to groupPosition+childPosition because of parameters passed in subsequent data callbacks
HorizontalScrollView scrollView_item = (HorizontalScrollView) view.findViewById(R.id.scrollView_item);
scrollView_item.setTag(groupPosition + "_" + childPosition);
//This is of course our SideSlide delete event
RelativeLayout img_delete = (RelativeLayout) view.findViewById(R.id.img_delete);
img_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null)
listener.OnChildDel(groupPosition, childPosition);
}
});
scrollView_item.setOnTouchListener(new OnTouchListener() {
private float startX = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// If there is an itemView with a Delete button, let it slide back and lock the touch. Unlocking occurs in the dispatchTouchEvent of the parent component
if (mScrollView != null) {
scrollView(mScrollView, HorizontalScrollView.FOCUS_LEFT);
mScrollView = null;
mLockOnTouch = true;
return true;
}
startX = event.getX();
//Press down, default start long press event
mLastMotionX = x;
mLastMotionY = y;
isMoved = false;
//Open the thread for event monitoring, which is the effect of the timer
handler.postDelayed(mLongPressRunnable, ViewConfiguration.getLongPressTimeout());
tag = (String) v.getTag();
break;
case MotionEvent.ACTION_MOVE:
if (isMoved) break;
//Detects whether press state moves, releases long press event threads if limit is exceeded
if (Math.abs(mLastMotionX - x) > TOUCH_SLOP
|| Math.abs(mLastMotionY - y) > TOUCH_SLOP_Y) {
//Move beyond threshold means move
isMoved = true;
handler.removeCallbacks(mLongPressRunnable);
iskey = true;
}
break;
case MotionEvent.ACTION_UP:
HorizontalScrollView view = (HorizontalScrollView) v;
//Get the horizontal coordinates when you release.
// Slide 100 pixels to the left and pop up the Delete button
if (startX > event.getX() + 100) {
startX = 0;// Restore startX value to prevent confusion because an event handling object is common
//Call the sliding method and move right
scrollView(view, HorizontalScrollView.FOCUS_RIGHT);
mScrollView = view;
// return true;
} else {
//Restore item state
if (Math.abs(event.getX()-startX)!=0) {
scrollView(view, HorizontalScrollView.FOCUS_LEFT);
}else{
//Click on an event to identify a long press event when iskey is false
if (listener != null && iskey) {
String TAG = (String) v.getTag();
String[] tags = TAG.split("_");
listener.OnChildClick(Integer.valueOf(tags[0]), Integer.valueOf(tags[1]));
}
}
}
//Of course, you need to cancel the long press event after lifting
handler.removeCallbacks(mLongPressRunnable);
iskey = true;
break;
}
return false;
}
});
return view;
}
Timer threads and variables are as follows
//Record coordinate information
private int mLastMotionX, mLastMotionY;
//Is it moved
private boolean isMoved;
//Long press runnable
private Runnable mLongPressRunnable;
//Moving threshold
private final int TOUCH_SLOP = 20;
//Y-Axis Moving Threshold
private final int TOUCH_SLOP_Y = 4;
//Distinguish click events from long press events
private boolean iskey = true;
private String tag;
//Long press runnable
private Runnable mLongPressRunnable = new Runnable() {
@Override
public void run() {
handler.sendMessage(new Message());
iskey = false;
}
};
Callback function for handling events in callback handle
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
try {
String[] tags = tag.split("_");
if (listener != null) {
if (tags.length == 2) {
//item Long By Event
listener.OnChildLongClick(Integer.valueOf(tags[0]), Integer.valueOf(tags[1]));
}
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
};
If necessary, you can write onTouch directly into the HorizontalScrollView custom control to improve the simplicity of the code. At the end of this function, long press events, side sliding deletions and click events are added, and group item s can also add side sliding, just like this