聊天界面每个条目的侧滑
思路:1.自定义一个View继承FrameLayout,
2.重写方法:
(1)computeScroll方法(持续滑动):
@Override
public void computeScroll() {
super.computeScroll();
if (mDragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this);
}
}
(2)onInterceptTouchEvent方法(传递触摸事件)
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG, "onInterceptTouchEvent: " + ev.getAction());
return mDragHelper.shouldInterceptTouchEvent(ev);
}
(3)onTouchEvent方法
@Override
public boolean onTouchEvent(MotionEvent event) {
try {
mDragHelper.processTouchEvent(event);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
(4)onLayout方法(摆放子View位置)
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//摆放位置
layoutContent(false);
}
private void layoutContent(boolean isOpen) {
//摆放前的View
Rect frontRect = computeFrontViewRect(isOpen);
mFrontView.layout(frontRect.left, frontRect.top, frontRect.right, frontRect.bottom);
//摆放后的View
Rect backRect = computeBackViewViaFront(frontRect);
mBackView.layout(backRect.left, backRect.top, backRect.right, backRect.bottom);
}
private Rect computeBackViewViaFront(Rect frontRect) {
int left = frontRect.right;
return new Rect(left, 0, left + mRange, 0 + mHeight);
}
private Rect computeFrontViewRect(boolean isOpen) {
int left = 0;
if (isOpen) {
left = -mRange;
}
return new Rect(0, 0, left + mWidth, 0 + mHeight);
}
(5)onFinishInflate方法(获取子View):
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//当XML被填充完毕时调用
mBackView = getChildAt(0);//两个TextView
mFrontView = getChildAt(1);//展示布局
}
(6)onSizeChange方法(测量View的大小)
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//测量
mHeight = mFrontView.getMeasuredHeight();
mWidth = mFrontView.getMeasuredWidth();
mRange = mBackView.getMeasuredWidth();
Log.i(TAG, "onFinishInflate: " + "Height:" + mHeight + " mWidth:" + mWidth);
}
3.在构造方法中初始化ViewDragHelper:ViewDragHelper.create(this,1.0f,mCallback);mCallback为ViewDragHelper.Callback对象,初始化它需要实现它的如下方法:
(1)tryCaptureView(),该方法返回true说明可以拖拽,这里两个子View都可以拖拽,所以直接返回true。
@Override
public boolean tryCaptureView(View child, int pointerId) {
//返回true为可以拖拽
return true;
}
(2)clampViewPositionHorizontal():限定水平移动的范围,返回的是View的左坐标
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//left
if (child==mFrontView){
if (left>0){
return 0;
}else if (left<-mRange){
return -mRange;
}
}else if (child==mBackView){
if (left>mWidth){
return mWidth;
}else if (left<mWidth-mRange){
return mWidth-mRange;
}
}
return left;
}
(3)onViewReleased():根据滑动速度和方向来执行打开或者关闭:
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
//xvel:- 为向左,+ 为向右,0 为没动
if (xvel==0&&releasedChild.getLeft()<-mRange/2){
//执行打开
open();
}else if (xvel<0){
open();
}else {
close();
}
}
(4)onViewPositionChanged():用来传递事件,并且根据状态去调用对应的接口方法: @Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
//传递事件
if (changedView == mFrontView) {
mBackView.offsetLeftAndRight(dx);
} else if (changedView == mBackView) {
mFrontView.offsetLeftAndRight(dx);
}
//根据不同的状态调用不同的监听方法
dispatchSwipeEvent();
//兼容老版本
invalidate();
}
};
private void dispatchSwipeEvent() {
if (mOnSwipeLayoutListener!=null){
mOnSwipeLayoutListener.onDraging(this);
}
//记录最后的状态
Status finalStatus=mStatus;
//获取当前的状态
mStatus=updateStatus();
if (mStatus!=finalStatus){
if (mOnSwipeLayoutListener!=null){
if (mStatus==Status.Close){
mOnSwipeLayoutListener.onClose(this);
}else if (mStatus==Status.Open){
mOnSwipeLayoutListener.onOpen(this);
}else if (mStatus==Status.Draging){
if (finalStatus==Status.Close){
mOnSwipeLayoutListener.onStartOpen(this);
}else if (finalStatus== Status.Open){
mOnSwipeLayoutListener.onStartClose(this);
}
}
}
}
}
4.创建接口: public static interface OnSwipeLayoutListener{
void onClose(SwipeLayout swipeLayout);
void onOpen(SwipeLayout swipeLayout);
void onDraging(SwipeLayout swipeLayout);
void onStartClose(SwipeLayout swipeLayout);
void onStartOpen(SwipeLayout swipeLayout);
}
5.监听设置:
public void setOnSwipeLayoutListener(OnSwipeLayoutListener onSwipeLayoutListener) {
mOnSwipeLayoutListener = onSwipeLayoutListener;
}
这样便可以实现侧滑,并且应用该View后,在不同状态可以在相应的监听方法中执行想要执行的。
完整的代码:
public class SwipeLayout extends FrameLayout {
private String TAG = "SENDI";
private ViewDragHelper mDragHelper;
private View mBackView;
private View mFrontView;
private int mHeight;
private int mWidth;
private int mRange;
private Status mStatus=Status.Close;
private OnSwipeLayoutListener mOnSwipeLayoutListener;
public OnSwipeLayoutListener getOnSwipeLayoutListener() {
return mOnSwipeLayoutListener;
}
public void setOnSwipeLayoutListener(OnSwipeLayoutListener onSwipeLayoutListener) {
mOnSwipeLayoutListener = onSwipeLayoutListener;
}
public Status getStatus() {
return mStatus;
}
public void setStatus(Status status) {
mStatus = status;
}
public static enum Status{
Close,Open,Draging;
}
public static interface OnSwipeLayoutListener{
void onClose(SwipeLayout swipeLayout);
void onOpen(SwipeLayout swipeLayout);
void onDraging(SwipeLayout swipeLayout);
void onStartClose(SwipeLayout swipeLayout);
void onStartOpen(SwipeLayout swipeLayout);
}
public SwipeLayout(Context context) {
this(context, null);
}
public SwipeLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SwipeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//a.初始化
mDragHelper = ViewDragHelper.create(this, 1.0f, mCallback);
}
ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
//重写监听
@Override
public boolean tryCaptureView(View child, int pointerId) {
//返回true为可以拖拽
return true;
}
//限定移动范围
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//left
if (child==mFrontView){
if (left>0){
return 0;
}else if (left<-mRange){
return -mRange;
}
}else if (child==mBackView){
if (left>mWidth){
return mWidth;
}else if (left<mWidth-mRange){
return mWidth-mRange;
}
}
return left;
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
//xvel:- 为向左,+ 为向右,0 为没动
if (xvel==0&&releasedChild.getLeft()<-mRange/2){
//执行打开
open();
}else if (xvel<0){
open();
}else {
close();
}
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
//传递事件
if (changedView == mFrontView) {
mBackView.offsetLeftAndRight(dx);
} else if (changedView == mBackView) {
mFrontView.offsetLeftAndRight(dx);
}
//根据不同的状态调用不同的监听方法
dispatchSwipeEvent();
//兼容老版本
invalidate();
}
};
private void dispatchSwipeEvent() {
if (mOnSwipeLayoutListener!=null){
mOnSwipeLayoutListener.onDraging(this);
}
//记录最后的状态
Status finalStatus=mStatus;
//获取当前的状态
mStatus=updateStatus();
if (mStatus!=finalStatus){
if (mOnSwipeLayoutListener!=null){
if (mStatus==Status.Close){
mOnSwipeLayoutListener.onClose(this);
}else if (mStatus==Status.Open){
mOnSwipeLayoutListener.onOpen(this);
}else if (mStatus==Status.Draging){
if (finalStatus==Status.Close){
mOnSwipeLayoutListener.onStartOpen(this);
}else if (finalStatus== Status.Open){
mOnSwipeLayoutListener.onStartClose(this);
}
}
}
}
}
private Status updateStatus() {
int left=mFrontView.getLeft();
if (left==0){
return Status.Close;
}else if (left==-mRange){
return Status.Open;
}
return Status.Draging;
}
private void open() {
Utils.showToast(getContext(),"open");
open(true);
}
public void open(boolean isSmooth){
int finalLeft=-mRange;
if (isSmooth){
// 开始动画
if (mDragHelper.smoothSlideViewTo(mFrontView,finalLeft,0)){
ViewCompat.postInvalidateOnAnimation(this);
}
}else {
layoutContent(true);
}
}
private void close(){
Utils.showToast(getContext(),"close");
close(true);
}
public void close(boolean isSmooth){
int finalLeft=0;
if (isSmooth){
// 开始动画
if (mDragHelper.smoothSlideViewTo(mFrontView,finalLeft,0)){
ViewCompat.postInvalidateOnAnimation(this);
}else {
layoutContent(false);
}
}
}
@Override
public void computeScroll() {
super.computeScroll();
if (mDragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this);
}
}
//传递触摸事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG, "onInterceptTouchEvent: " + ev.getAction());
return mDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
try {
mDragHelper.processTouchEvent(event);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//摆放位置
layoutContent(false);
}
private void layoutContent(boolean isOpen) {
//摆放前的View
Rect frontRect = computeFrontViewRect(isOpen);
mFrontView.layout(frontRect.left, frontRect.top, frontRect.right, frontRect.bottom);
//摆放后的View
Rect backRect = computeBackViewViaFront(frontRect);
mBackView.layout(backRect.left, backRect.top, backRect.right, backRect.bottom);
}
private Rect computeBackViewViaFront(Rect frontRect) {
int left = frontRect.right;
return new Rect(left, 0, left + mRange, 0 + mHeight);
}
private Rect computeFrontViewRect(boolean isOpen) {
int left = 0;
if (isOpen) {
left = -mRange;
}
return new Rect(0, 0, left + mWidth, 0 + mHeight);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//当XML被填充完毕时调用
mBackView = getChildAt(0);//两个TextView
mFrontView = getChildAt(1);//展示布局
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//测量
mHeight = mFrontView.getMeasuredHeight();
mWidth = mFrontView.getMeasuredWidth();
mRange = mBackView.getMeasuredWidth();
Log.i(TAG, "onFinishInflate: " + "Height:" + mHeight + " mWidth:" + mWidth);
}
}