Android new Inbox app style listview with swipe left and right

前端 未结 3 404
暗喜
暗喜 2020-12-24 13:53

m trying to build android new inbox style listview with swipe left and right as shown in this image , i have tried 47deg swipelistview but its not that stable , is there any

相关标签:
3条回答
  • 2020-12-24 14:06

    Updated Answer

    As I mentioned previously, I took the same approach and it seems to work as expected. I have added 3 layers to a RelativeLayout. Top layer is what you want to show. Second layer is a plain background with delete icon at the left. Third layer is another plain background with share icon at the right. I implemented a swipe detector class which extends View.OnTouchListener.

    public class SwipeDetector implements View.OnTouchListener {
    
        private static final int MIN_DISTANCE = 300;
        private static final int MIN_LOCK_DISTANCE = 30; // disallow motion intercept
        private boolean motionInterceptDisallowed = false;
        private float downX, upX;
        private ObjectHolder holder;
        private int position;
    
        public SwipeDetector(ObjectHolder h, int pos) {
            holder = h;
            position = pos;
        }
    
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                downX = event.getX();
                return true; // allow other events like Click to be processed
            }
    
            case MotionEvent.ACTION_MOVE: {
                upX = event.getX();
                float deltaX = downX - upX;
    
                if (Math.abs(deltaX) > MIN_LOCK_DISTANCE && listView != null && !motionInterceptDisallowed) {
                    listView.requestDisallowInterceptTouchEvent(true);
                    motionInterceptDisallowed = true;
                }
    
                if (deltaX > 0) {
                    holder.deleteView.setVisibility(View.GONE);
                } else {
                    // if first swiped left and then swiped right
                    holder.deleteView.setVisibility(View.VISIBLE);
                }
    
                swipe(-(int) deltaX);
                return true;
            }
    
            case MotionEvent.ACTION_UP:
                upX = event.getX();
                float deltaX = upX - downX;
                if (Math.abs(deltaX) > MIN_DISTANCE) {
                    // left or right
                    swipeRemove();
                } else {
                    swipe(0);
                }
    
                if (listView != null) {
                    listView.requestDisallowInterceptTouchEvent(false);
                    motionInterceptDisallowed = false;
                }
    
                holder.deleteView.setVisibility(View.VISIBLE);
                return true;
    
            case MotionEvent.ACTION_CANCEL:
                holder.deleteView.setVisibility(View.VISIBLE);
                return false;
            }
    
        return true;
        }
    
        private void swipe(int distance) {
            View animationView = holder.mainView;
            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) animationView.getLayoutParams();
            params.rightMargin = -distance;
            params.leftMargin = distance;
            animationView.setLayoutParams(params);
        }
    
        private void swipeRemove() {
            remove(getItem(position));
            notifyDataSetChanged();
        }
    }
    


    public static class ObjectHolder {
        public LinearLayout mainView;
        public RelativeLayout deleteView;
        public RelativeLayout shareView;
    
        /* other views here */
    }
    

    I have also added requestDisallowInterceptTouchEvent so that ListView (which is parent) doesn't intercept the touch event when there's some amount of vertical scrolling involved.

    I have written a blogpost about it which you can find it here. I have also added a Youtube video for demo.


    Old Answer

    I implemented one of these myself, but it's a bit different. I use just touch instead of swiping. Touch to open, touch to close. Here's youtube demo.

    I created custom ArrayAdapter. To set the layout, I created a custom layout like this.

    <RelativeLayout>
         <RelativeLayout>
              <Stuff that you want at the back of your list/>
         </RelativeLayout>
         <RelativeLayout>
              <Stuff that you want at the front of your list/>
         </RelativeLayout>
    </RelativeLayout>
    

    Using RelativeLayout, I am putting the top view over the bottom view. Both have same sizes. You can use different layouts for inner layouts.

    In Custom ArrayAdapter,

    @Override
    public view getView(int position, View convertView, ViewGroup parent) {
        // get holder and entry
        // set each element based on entry preferences
    
    
    
        holder.topView.setOnClickListener(new View.OnClickListener() {
    
              @Override
              public void onClick(View v) {
                  if (entry.isSwiped()) {
                      swipeWithAnimationValue(holder.topView, 1);
                      entry.setSwiped(false);
                  } else {
                      closeOtherSwipes(entry);  // if you want to keep only one entry open at a time
                      swipeWithAnimationValue(holder.topView, 0);
                      entry.setSwiped(true);
                  }
             }
       });
    }
    

    Normal Animation would not work as it just shifts the view, but it's still there so if you try to click, the click still occurs on the top view. Hence I have used valueAnimator and actually shifted those lists.

    public void swipeWithAnimationValue(final View view, final int direction) {
        final int width = view.getWidth();
        Log.i(TAG, "view width = " + String.valueOf(width));
        ValueAnimator animationSwipe;
        int duration = 300;
        if (direction == 0) {
             animationSwipe = ValueAnimator.ofInt(0, view.getWidth() - 200);
        } else {
             animationSwipe = ValueAnimator.ofInt(view.getWidth() - 200, 0);
        }
    
        animationSwipe.setDuration(duration);
        AnimatorUpdateListener maringUpdater = new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
    
                 RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
                 params.rightMargin = -(Integer)animation.getAnimatedValue();
                 params.leftMargin = (Integer)animation.getAnimatedValue();
                 view.setLayoutParams(params);
            }
        };
    
        animationSwipe.addUpdateListener(maringUpdater);
        animationSwipe.setRepeatCount(0);
        animationSwipe.start();
    }
    
    0 讨论(0)
  • 2020-12-24 14:17

    Instead of using a custom ListView you can simply support "swipe" gesture on list items onTouch, like the following:

    private static final int DEFAULT_THRESHOLD = 128;
    
    row.setOnTouchListener(new View.OnTouchListener() {
    
        int initialX = 0;
        final float slop = ViewConfiguration.get(context).getScaledTouchSlop();
    
        public boolean onTouch(final View view, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                initialX = (int) event.getX();
                view.setPadding(0, 0, 0, 0);
            } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                int currentX = (int) event.getX();
                int offset = currentX - initialX;
                if (Math.abs(offset) > slop) {
                    view.setPadding(offset, 0, 0, 0);
    
                    if (offset > DEFAULT_THRESHOLD) {
                        // TODO :: Do Right to Left action! And do nothing on action_up.
                    } else if (offset < -DEFAULT_THRESHOLD) {
                        // TODO :: Do Left to Right action! And do nothing on action_up.
                    }
                }
            } else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
                // Animate back if no action was performed.
                ValueAnimator animator = ValueAnimator.ofInt(view.getPaddingLeft(), 0);
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                        view.setPadding((Integer) valueAnimator.getAnimatedValue(), 0, 0, 0);
                    }
                });
                animator.setDuration(150);
                animator.start();
            }
    };
    

    I also use reverse animation if no action was performed.

    This solution is lightweight so you should not experience any lags.

    0 讨论(0)
  • 2020-12-24 14:23

    Check out: SwipeActionAdapter

    It's a great library that does exactly what you're asking for. It allows Swipe in both directions with an underlying Layout or Color. It's easy to implement and looks nice!

    0 讨论(0)
提交回复
热议问题