렌더링:
인터넷에는 2개의 뷰가 합쳐져 있는데, 기본적으로 가로로 이동할 때는 오른쪽 뷰가 표시됩니다. 다만, QQ 최신 버전에서의 효과는 이렇지는 않지만 느낌이 매우 좋기 때문에 모방도가 높은 것을 사용하는 것이 좋습니다.
지식 포인트:
1. ViewDragHelper 사용
2. 사용자 정의된 뷰 그룹;
int dx, int dy): 뷰가 캡처되고 드래그 또는 설정으로 인해 위치가 변경됩니다
public SwipeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { viewDragHelper = ViewDragHelper.create(this, callback); } public boolean onInterceptTouchEvent(MotionEvent ev) { boolean result = viewDragHelper.shouldInterceptTouchEvent(ev); } public boolean onTouchEvent(MotionEvent event) { viewDragHelper.processTouchEvent(event); return true; }1) 생성자에서 2) 생성하고 onInterceptTouchEvent에서 가로채기3) onTouchEvent에서 이벤트가 나옵니다자, 가장 이해하기 어려운 부분이 해결되었습니다. 다음으로 구체적인 구현을 살펴보겠습니다. 먼저 레이아웃을 살펴보겠습니다.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <scrollviewgroup.lly.com.swiplayout.SwipeLayout android:id="@+id/swipeLayout" android:layout_width="match_parent" android:layout_height="wrap_content" > <!-- delete区域的布局 --> <include layout="@layout/layout_delete" /> <!-- item内容的布局 --> <include layout="@layout/layout_content" /> </scrollviewgroup.lly.com.swiplayout.SwipeLayout> </LinearLayout>이에 대해 말할 것도 없습니다. 사용자 정의 뷰 그룹에는 두 개의 하위 컨트롤이 포함되어 있습니다. 그런 다음 SwipeLayout이 어떻게 구현되는지 살펴보겠습니다.
@Override protected void onFinishInflate() { super.onFinishInflate(); deleteView = getChildAt(0); contentView = getChildAt(1); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); deleteHeight = deleteView.getMeasuredHeight(); deleteWidth = deleteView.getMeasuredWidth(); contentWidth = contentView.getMeasuredWidth(); screenWidth = getWidth(); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { // super.onLayout(changed, left, top, right, bottom); deleteView.layout(screenWidth - deleteWidth, 0, (screenWidth - deleteWidth) + deleteWidth, deleteHeight); contentView.layout(0, 0, contentWidth, deleteHeight); }위 코드는 onlayout 내부에 중점을 두고 있습니다. 여기서는 먼저 deleteView를 그립니다. He를 오른쪽에 두고 상단에 contentView 레이어를 변경하여 표시될 때 contentView만 표시되도록 합니다. 다음으로 ontouch 방식을 살펴보겠습니다
public boolean onTouchEvent(MotionEvent event) { //如果当前有打开的,则下面的逻辑不能执行 if(!SwipeLayoutManager.getInstance().isShouldSwipe(this)){ requestDisallowInterceptTouchEvent(true); return true; } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downX = event.getX(); downY = event.getY(); break; case MotionEvent.ACTION_MOVE: //1.获取x和y方向移动的距离 float moveX = event.getX(); float moveY = event.getY(); float delatX = moveX - downX;//x方向移动的距离 float delatY = moveY - downY;//y方向移动的距离 if(Math.abs(delatX)>Math.abs(delatY)){ //表示移动是偏向于水平方向,那么应该SwipeLayout应该处理,请求父view不要拦截 requestDisallowInterceptTouchEvent(true); } //更新downX,downY downX = moveX; downY = moveY; break; case MotionEvent.ACTION_UP: break; } viewDragHelper.processTouchEvent(event); return true; }위 내용은 주로 이벤트 충돌을 수평으로 이동할 때 이를 가로채지 않도록 부모 뷰에 요청합니다. 다음은 핵심입니다
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() { @Override public boolean tryCaptureView(View child, int pointerId) { return child==contentView; } @Override public int getViewHorizontalDragRange(View child) { return deleteWidth; } @Override public int clampViewPositionHorizontal(View child, int left, int dx) { if(child==contentView){ if(left>0)left = 0; if(left<-deleteWidth)left = -deleteWidth; } return left; } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); //判断开和关闭的逻辑 if(contentView.getLeft()==0 && currentState!=SwipeState.Close){ //说明应该将state更改为关闭 currentState = SwipeState.Close; //回调接口关闭的方法 if(listener!=null){ listener.onClose(getTag()); } //说明当前的SwipeLayout已经关闭,需要让Manager清空一下 SwipeLayoutManager.getInstance().clearCurrentLayout(); }else if (contentView.getLeft()==-deleteWidth && currentState!=SwipeState.Open) { //说明应该将state更改为开 currentState = SwipeState.Open; //回调接口打开的方法 if(listener!=null){ listener.onOpen(getTag()); } //当前的Swipelayout已经打开,需要让Manager记录一下下 SwipeLayoutManager.getInstance().setSwipeLayout(SwipeLayout.this); } } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); if(contentView.getLeft()<-deleteWidth/2){ //应该打开 open(); }else { //应该关闭 close(); } } };처음에 위 코드에서 메서드를 모두 언급했습니다. tryCaptureView에서 contentView를 슬라이드하도록 허용합니다. getViewHorizontalDragRange Zhongquedong의 슬라이딩 범위는 CLAMPViewPositionHorizontal에서 제한됩니다. 상태는 onViewPositionChanged에서 업데이트됩니다. 마지막으로 손가락을 떼면 뷰가 자동으로 롤백됩니다.
/** * 打开的方法 */ public void open() { viewDragHelper.smoothSlideViewTo(contentView,-deleteWidth,contentView.getTop()); ViewCompat.postInvalidateOnAnimation(SwipeLayout.this); } /** * 关闭的方法 */ public void close() { viewDragHelper.smoothSlideViewTo(contentView,0,contentView.getTop()); ViewCompat.postInvalidateOnAnimation(SwipeLayout.this); }; public void computeScroll() { if(viewDragHelper.continueSettling(true)){ ViewCompat.postInvalidateOnAnimation(this); } }ComputeScroll 메서드를 다시 작성하세요. 그렇지 않으면 슬라이딩 효과가 더 이상 움직이지 않습니다. 이제 사용자 정의 프레임 레이아웃이 완성되었습니다하지만 슬라이드 아웃된 뷰에서 위아래로 슬라이드할 때 이 뷰의 deleteView가 계속 표시되므로 문제를 발견했습니다. 여전히 액티비티에 추가해야 합니다. 처리해 보겠습니다.
recyView.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); if(dy>0 || dy<0){ SwipeLayoutManager.getInstance().closeCurrentLayout(); } } });이 RecyclerView가 위아래로 슬라이드되면 하위 뷰를 재설정하세요.
하루만 기다리세요.