先看UI效果图
主要是实现侧滑操作,通过ViewDragHelper来实现侧滑。
<com.hu.test.wight.SwipeListLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/sll_msg"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:orientation="horizontal" >
<TextView
android:id="@+id/tv_top"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="#66ff0000"
android:gravity="center"
android:text="置顶" />
<TextView
android:id="@+id/tv_delete"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="#330000ff"
android:gravity="center"
android:text="删除" />
LinearLayout>
<LinearLayout
android:id="@+id/ll_msg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:background="#66ffffff"
android:orientation="horizontal" >
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:padding="15dp"
android:text="" />
LinearLayout>
com.hu.test.wight.SwipeListLayout>
这里用到的就是自定义空间,继承FrameLayout布局,并进行位置处理,看布局显示位置如下:
由上面的图看出,必须要对布局进行代码控制,才能让侧滑布局在布局外面(FrameLayout仅仅通过xml不好实现)
这里先了解下View生命周期的顺序:
注意到 onFinishInflate()这个方法,指的是:当系统解析完View之后调用。所以可以在这里提前获取组件的View
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//布局只支持拥有两个子布局
hiddenView = getChildAt(0); // 得到隐藏按钮的linearlayout
itemView = getChildAt(1); // 得到最上层的linearlayout
}
然后在onSizeChanged()这里测量view的宽高
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 测量子View的长和宽
itemWidth = itemView.getMeasuredWidth();
itemHeight = itemView.getMeasuredHeight();
hiddenViewWidth = hiddenView.getMeasuredWidth();
hiddenViewHeight = hiddenView.getMeasuredHeight();
}
调用onLayout()来确定初始化得位置,达到侧滑布局在屏幕外面
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
layout(Status.Close);//初始化位置,状态为关闭
}
/**
*确定侧滑区域的位置
* @param status
*/
private void layout(Status status) {
if (status == Status.Close) {
hiddenView.layout(itemWidth, 0, itemWidth + hiddenViewWidth,
itemHeight);
itemView.layout(0, 0, itemWidth, itemHeight);
} else {
hiddenView.layout(itemWidth - hiddenViewWidth, 0, itemWidth,
itemHeight);
itemView.layout(-hiddenViewWidth, 0, itemWidth - hiddenViewWidth,
itemHeight);
}
}
2.ViewDragHelper的使用
创建实例: 两个参数:一个操作的view,和回调callback
public SwipeListLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mDragHelper = ViewDragHelper.create(this, callback);
}
使用ViewDragHelper先把onTouch事件处理好,以免忘记了。
// 让ViewDragHelper来处理触摸事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
if (action == MotionEvent.ACTION_CANCEL) {
mDragHelper.cancel();
return false;
}
return mDragHelper.shouldInterceptTouchEvent(ev);
}
// 让ViewDragHelper来处理触摸事件
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragHelper.processTouchEvent(event);
return true;
};
Callback的回调处理:
// ViewDragHelper的回调
Callback callback = new Callback() {
@Override
public boolean tryCaptureView(View view, int arg1) {
return view == itemView;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if (child == itemView) {
if (left > 0) {
return 0;
} else {
left = Math.max(left, -hiddenViewWidth);
return left;
}
}
return 0;
}
@Override
public int getViewHorizontalDragRange(View child) {
return hiddenViewWidth;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
if (itemView == changedView) {
hiddenView.offsetLeftAndRight(dx);
}
// 有时候滑动很快的话 会出现隐藏按钮的linearlayout没有绘制的问题
// 为了确保绘制成功 调用 invalidate
invalidate();
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
//失去焦点的回弹方法
// 向右滑xvel为正 向左滑xvel为负
if (releasedChild == itemView) {
if (xvel == 0
&& Math.abs(itemView.getLeft()) > hiddenViewWidth / 2.0f) {
open(smooth);
} else if (xvel < 0) {
open(smooth);
} else {
close(smooth);
}
}
}
};
自定义的全部代码如下:
package com.hu.test.wight;
import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.support.v4.widget.ViewDragHelper.Callback;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
/**
* 侧滑Layout
*/
public class SwipeListLayout extends FrameLayout {
private View hiddenView;
private View itemView;
private int hiddenViewWidth;
private ViewDragHelper mDragHelper;
private int hiddenViewHeight;
private int itemWidth;
private int itemHeight;
private OnSwipeStatusListener listener;
private Status status = Status.Close;
private boolean smooth = true;
public static final String TAG = "SlipListLayout";
// 状态
public enum Status {
Open, Close
}
/**
* 设置侧滑状态
*
* @param status
* 状态 Open or Close
* @param smooth
* 若为true则有过渡动画,否则没有
*/
public void setStatus(Status status, boolean smooth) {
this.status = status;
if (status == Status.Open) {
open(smooth);
} else {
close(smooth);
}
}
public void setOnSwipeStatusListener(OnSwipeStatusListener listener) {
this.listener = listener;
}
/**
* 是否设置过渡动画
*
* @param smooth
*/
public void setSmooth(boolean smooth) {
this.smooth = smooth;
}
public interface OnSwipeStatusListener {
/**
* 当状态改变时回调
*
* @param status
*/
void onStatusChanged(Status status);
/**
* 开始执行Open动画
*/
void onStartCloseAnimation();
/**
* 开始执行Close动画
*/
void onStartOpenAnimation();
}
public SwipeListLayout(Context context) {
this(context, null);
}
public SwipeListLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mDragHelper = ViewDragHelper.create(this, callback);
}
// ViewDragHelper的回调
Callback callback = new Callback() {
@Override
public boolean tryCaptureView(View view, int arg1) {
return view == itemView;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
if (child == itemView) {
if (left > 0) {
return 0;
} else {
left = Math.max(left, -hiddenViewWidth);
return left;
}
}
return 0;
}
@Override
public int getViewHorizontalDragRange(View child) {
return hiddenViewWidth;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
if (itemView == changedView) {
hiddenView.offsetLeftAndRight(dx);
}
// 有时候滑动很快的话 会出现隐藏按钮的linearlayout没有绘制的问题
// 为了确保绘制成功 调用 invalidate
invalidate();
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
// 向右滑xvel为正 向左滑xvel为负
if (releasedChild == itemView) {
if (xvel == 0
&& Math.abs(itemView.getLeft()) > hiddenViewWidth / 2.0f) {
open(smooth);
} else if (xvel < 0) {
open(smooth);
} else {
close(smooth);
}
}
}
};
private Status preStatus = Status.Close;
/**
* 侧滑关闭
*
* @param smooth
* 为true则有平滑的过渡动画
*/
private void close(boolean smooth) {
preStatus = status;
status = Status.Close;
if (smooth) {
if (mDragHelper.smoothSlideViewTo(itemView, 0, 0)) {
if (listener != null) {
Log.i(TAG, "start close animation");
listener.onStartCloseAnimation();
}
ViewCompat.postInvalidateOnAnimation(this);
}
} else {
layout(status);
}
if (listener != null && preStatus == Status.Open) {
Log.i(TAG, "close");
listener.onStatusChanged(status);
}
}
/**
*
* @param status
*/
private void layout(Status status) {
if (status == Status.Close) {
hiddenView.layout(itemWidth, 0, itemWidth + hiddenViewWidth,
itemHeight);
itemView.layout(0, 0, itemWidth, itemHeight);
} else {
hiddenView.layout(itemWidth - hiddenViewWidth, 0, itemWidth,
itemHeight);
itemView.layout(-hiddenViewWidth, 0, itemWidth - hiddenViewWidth,
itemHeight);
}
}
/**
* 侧滑打开
*
* @param smooth
* 为true则有平滑的过渡动画
*/
private void open(boolean smooth) {
preStatus = status;
status = Status.Open;
if (smooth) {
if (mDragHelper.smoothSlideViewTo(itemView, -hiddenViewWidth, 0)) {
if (listener != null) {
Log.i(TAG, "start open animation");
listener.onStartOpenAnimation();
}
ViewCompat.postInvalidateOnAnimation(this);
}
} else {
layout(status);
}
if (listener != null && preStatus == Status.Close) {
Log.i(TAG, "open");
listener.onStatusChanged(status);
}
}
@Override
public void computeScroll() {
super.computeScroll();
// 开始执行动画
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
// 让ViewDragHelper来处理触摸事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
if (action == MotionEvent.ACTION_CANCEL) {
mDragHelper.cancel();
return false;
}
return mDragHelper.shouldInterceptTouchEvent(ev);
}
// 让ViewDragHelper来处理触摸事件
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragHelper.processTouchEvent(event);
return true;
};
@Override
protected void onFinishInflate() {
super.onFinishInflate();
hiddenView = getChildAt(0); // 得到隐藏按钮的linearlayout
itemView = getChildAt(1); // 得到最上层的linearlayout
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 测量子View的长和宽
itemWidth = itemView.getMeasuredWidth();
itemHeight = itemView.getMeasuredHeight();
hiddenViewWidth = hiddenView.getMeasuredWidth();
hiddenViewHeight = hiddenView.getMeasuredHeight();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
layout(Status.Close);
}
}
3 , 处理置顶和删除功能
recycleView 用的BaseQuickAdapter的适配库,习惯了
package com.hu.test.adapter;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.hu.test.R;
import com.hu.test.wight.SwipeListLayout;
import java.util.List;
/**
* Created by TT on 2017/9/7.
*/
public class MesageAdapter extends BaseQuickAdapter {
private Context mContext;
private int position;
private List data;
private SwipeListLayout swipeListLayout;
public MesageAdapter(Context context , List data) {
super(R.layout.item_msg, data);
this.mContext=context;
this.data=data;
}
@Override
protected void convert(final BaseViewHolder holder, final String item) {
swipeListLayout = holder.getView(R.id.sll_msg);
holder.setText(R.id.tv_name,item)
.setOnClickListener(R.id.tv_top, new View.OnClickListener() {
@Override
public void onClick(View view) {
btnOnClickListen.onTopListen(holder.getLayoutPosition(),swipeListLayout);
Log.e("position==",holder.getLayoutPosition()+"");
}
})
.setOnClickListener(R.id.tv_delete, new View.OnClickListener() {
@Override
public void onClick(View view) {
btnOnClickListen.onDeleteListen(holder.getLayoutPosition(),swipeListLayout);
Log.e("position==",holder.getLayoutPosition()+"");
}
})
.setOnClickListener(R.id.ll_msg, new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(mContext,"正常区域",Toast.LENGTH_SHORT).show();
Log.e("position==",holder.getLayoutPosition()+"");
}
});
}
//定义回调借口,将置顶按钮和删除按钮设置回调
public interface BtnOnClickListen{
void onTopListen(int position ,SwipeListLayout swipeListLayout);
void onDeleteListen(int position ,SwipeListLayout swipeListLayout);
}
/**
* 点击,设置回调
*/
public BtnOnClickListen btnOnClickListen;
/**
* 设置监听接口
* @param btnOnClickListen
*/
public void setBtnOnClickListen(BtnOnClickListen btnOnClickListen) {
this.btnOnClickListen = btnOnClickListen;
}
}
package com.hu.test.ui.drawer.message;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.ImageView;
import android.widget.Toast;
import com.hu.test.R;
import com.hu.test.adapter.MesageAdapter;
import com.hu.test.ui.BaseActivity;
import com.hu.test.wight.SwipeListLayout;
import java.util.ArrayList;
import java.util.List;
/**
* Created by TT on 2017/6/29.
*/
public class MessageActivity extends BaseActivity {
private ImageView iv;
private RecyclerView recyclerView;
private List stringList = new ArrayList<>();
private MesageAdapter mesageAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_message);
showContentView();
recyclerView = (RecyclerView) findViewById(R.id.rc_msg);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
initData();
}
private void initData() {
for (int i = 0; i < 10; i++) {
stringList.add("dassssssd"+i*10000);
}
mesageAdapter = new MesageAdapter(this,stringList);
recyclerView.setAdapter(mesageAdapter);
//置顶和删除的回调处理
mesageAdapter.setBtnOnClickListen(new MesageAdapter.BtnOnClickListen() {
@Override
public void onTopListen(int position, SwipeListLayout swipeListLayout) {
swipeListLayout.setStatus(SwipeListLayout.Status.Close, true);
//置顶操作就是:删除当前item,再加入到集合顶部,刷新就行
String zhiding = stringList.get(position);
stringList.remove(position);
mesageAdapter.notifyItemRemoved(position);
mesageAdapter.notifyItemRangeChanged(0,stringList.size()-position);
stringList.add(0,"置顶"+zhiding);
mesageAdapter.notifyItemInserted(0);
mesageAdapter.notifyItemRangeChanged(0,stringList.size()-0);
Toast.makeText(getApplicationContext(),"置顶",Toast.LENGTH_SHORT).show();
}
@Override
public void onDeleteListen(int position, SwipeListLayout swipeListLayout) {
swipeListLayout.setStatus(SwipeListLayout.Status.Close, true);
Toast.makeText(getApplicationContext(),"删除=="+"当前条目"+position,Toast.LENGTH_SHORT).show();
stringList.remove(position);
mesageAdapter.notifyItemRemoved(position);
mesageAdapter.notifyItemRangeChanged(0,stringList.size()-position);
}
});
}
}
当前效果就完成了。