6

Have u seen ios imessage app. In this app to see the timestamps of chat messages we need to swipe left. Initially they are hidden but when we swipe left the hidden timestamps are revealed. I would like to implement this in my android app as well.

enter image description here

When we swipe left the messages in grey bubbles remain fixed, however, messages in blue bubbles slide to left to reveal timestamp.

Below is what I have tried so far.

SwipeDetector.java - It detect swipes

public class SwipeDetector implements View.OnTouchListener {

private SwipeListener swipeListener;
private int hundred;

public static enum Action {
    LR, // Left to right
    RL, // Right to left
    TB, // Top to bottom
    BT, // Bottom to top
    None // Action not found
}

private static final int HORIZONTAL_MIN_DISTANCE = 30; // The minimum
// distance for
// horizontal swipe
private static final int VERTICAL_MIN_DISTANCE = 80; // The minimum distance
// for vertical
// swipe
private float downX, downY, upX, upY; // Coordinates
private Action mSwipeDetected = Action.None; // Last action

public SwipeDetector(Context context) {
    hundred = (int) context.getResources().getDimension(R.dimen.hundred);
}

public boolean swipeDetected() {
    return mSwipeDetected != Action.None;
}

public Action getAction() {
    return mSwipeDetected;
}

/**
 * Swipe detection
 */@Override
public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
        {
            downX = event.getX();
            downY = event.getY();
            mSwipeDetected = Action.None;
            return false; // allow other events like Click to be processed
        }
        case MotionEvent.ACTION_MOVE:
        {
            upX = event.getX();
            upY = event.getY();

            float deltaX = downX - upX;
            float deltaY = downY - upY;

            float absX = Math.abs(deltaX);
            float absY = Math.abs(deltaY);

            if((absX >= (3 * absY)) && absX <= hundred) {
                if (deltaX > 0) {
                    mSwipeDetected = Action.RL;
                    swipeListener.onSwipe(MotionEvent.ACTION_MOVE, Action.RL, absX);
                    return false;
                }
            }
            return false;
        }
        case MotionEvent.ACTION_UP:
            swipeListener.onSwipe(MotionEvent.ACTION_UP, Action.BT, 0);
            return false;
    }
    return false;
}

/**
 * Set chat send listener
 * @param listener
 */
public void setSwipeListener(SwipeListener listener) {
    swipeListener = listener;
}

public interface SwipeListener {
    void onSwipe(int event, Action action, float x);
}

}

SwipeActivity.java - Its the Activity class

public class SwipeActivity extends AppCompatActivity implements SwipeDetector.SwipeListener {

private ListView mListView;
private SwipeListAdapter mAdapter;

private ArrayList<String> mData = new ArrayList<>();
private SwipeDetector.SwipeListener mSwipeListener;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_swipe);

    mSwipeListener = this;

    for (int i=0; i<50; i++) {
        mData.add("Text");
    }

    mListView = (ListView) findViewById(R.id.listview);
    mAdapter = new SwipeListAdapter(SwipeActivity.this, mData);
    mListView.setAdapter(mAdapter);

    mListView.requestDisallowInterceptTouchEvent(true);

    // Set the touch listener
    final SwipeDetector swipeDetector = new SwipeDetector(SwipeActivity.this);
    swipeDetector.setSwipeListener(mSwipeListener);
    mListView.setOnTouchListener(swipeDetector);
}

@Override
public void onSwipe(int event, SwipeDetector.Action action, float x) {
    System.out.println("swipe: " + action + " = " + x);
    swipeListView(event, x);
}

private void swipeListView(int event, float x) {
    updateListView(event, x);
}

/**
 * Update ListView using animation
 * @param event
 * @param x
 */
private void updateListView(int event, float x) {
    int start = mListView.getFirstVisiblePosition();
    int end = mListView.getLastVisiblePosition();
    for(int i=start, j=end; i<=j; i++) {
        View v = mListView.getChildAt(i - start);
        TextView tv = (TextView) v.findViewById(R.id.textviewright);
        TextView tvH = (TextView) v.findViewById(R.id.textviewhidden);

        switch (event) {
            case MotionEvent.ACTION_MOVE:
                tv.animate().translationX(-x).setDuration(0);
                tvH.animate().translationX(-x).setDuration(0);
                break;
            case MotionEvent.ACTION_UP:
                tv.animate().translationX(0).setDuration(500);
                tvH.animate().translationX(0).setDuration(500);
                break;
        }
    }
}
}

SwipeListAdapter.java - ListView adapter

public class SwipeListAdapter extends BaseAdapter {

private Context mContext;
private ArrayList<String> mData;

public SwipeListAdapter() {}

public SwipeListAdapter(Context context, ArrayList<String> data){
    this.mContext = context;
    this.mData = data;
}

@Override
public int getCount() {
    return mData.size();
}

@Override
public Object getItem(int position) {
    return mData.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

private class ViewHolder {
    TextView mLeftText;
    TextView mRightText;
    TextView mHiddenText;
}

@Override
public View getView(final int position, View view, ViewGroup viewGroup) {
    ViewHolder holder = null;
    if (view == null) {
        holder = new ViewHolder();
        LayoutInflater mInflater = (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        view = mInflater.inflate(R.layout.item_list_swipe, null);

        holder.mLeftText =  (TextView) view.findViewById(R.id.textviewleft);
        holder.mRightText =  (TextView) view.findViewById(R.id.textviewright);
        holder.mHiddenText =  (TextView) view.findViewById(R.id.textviewhidden);

        view.setTag(holder);
    } else {
        holder = (ViewHolder) view.getTag();
    }

    holder.mRightText.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(mContext, "Right", Toast.LENGTH_SHORT).show();
        }
    });

    holder.mLeftText.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(mContext, "Left", Toast.LENGTH_SHORT).show();
        }
    });

    String text = mData.get(position);
    holder.mLeftText.setText(text + " " + position);
    holder.mRightText.setText(text + " " + position);

    return view;
}
}

activity_swipe.xml - xml for SwipeActivity

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ListView
    android:id="@+id/listview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

</LinearLayout>

item_list_swipe.xml - ListView item/row xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
    android:id="@+id/textviewleft"
    android:layout_width="wrap_content"
    android:layout_height="56dp"
    android:background="#FF0000"
    android:layout_alignParentLeft="true"
    android:gravity="center_vertical"
    android:padding="16dp"
    android:text="TextView"
    android:visibility="visible"/>

<TextView
    android:id="@+id/textviewright"
    android:layout_width="wrap_content"
    android:layout_height="56dp"
    android:background="#00FF00"
    android:layout_toLeftOf="@+id/textviewhidden"
    android:gravity="center_vertical"
    android:padding="16dp"
    android:text="TextView"
    android:visibility="visible"/>


<TextView
    android:id="@+id/textviewhidden"
    android:layout_width="100dp"
    android:layout_height="56dp"
    android:background="#0000FF"
    android:layout_alignParentRight="true"
    android:gravity="center"
    android:layout_marginRight="-100dp"
    android:padding="16dp"
    android:text="Date"
    android:visibility="visible"/>

</RelativeLayout>

Using the above code I am able to show the hidden TextView on the basis of pixels swiped. However, there are some problems. Below are my issues.

  1. Suppose I started the swipe and the hidden TextView is partially/half visible. Before the hidden TextView becomes completely visible I scrolled the ListView, now the ListView started scrolling. Now if again I will start the swipe, the rest/other half of the hidden TextView is not becoming visible. How can I handle this? If its not possible to handle then atleast tell me how to disable the ListView scroll when the swipe is started and enable the ListView scroll once swipe is stopped.

  2. Second issue is, if I start the swipe from the empty area of the ListView i.e. middle portion of left and right TextView then the swipe works perfectly fine. However, if I start the swipe from the right or left TextView then instead of the swipe, the click listener of the TextViews is fired. When I click on the right or left TextView then their onClickListener should get fired and when I swipe then the ListView's swipe should get fired.

Please help me out with this implementation.

Note: This is not like traditional ListView swipe functionality. In traditional ListView swipe, complete row is swiped to the left and the hidden view is revealed. In my case I want only part of the row (blue bubbles) to slide to reveal the hidden view (timestamp). Grey bubbles stay fixed. And I want this on all the rows of ListView not just single row. For traditional ListView swipe there are a lot of libraries out there like SwipeMenuListView, android-swipelistview and AndroidSwipeLayout and many others, but these doesn't fulfill my requirement.

Nitesh Kumar
  • 5,370
  • 5
  • 37
  • 54
  • I guess you are looking something like [SwipeMenuListView](https://android-arsenal.com/details/1/912) or [SwipeListView](https://android-arsenal.com/details/1/255) – Kunu Sep 17 '15 at 10:02
  • Maby its good time to start with android latest library- RecycleView which support many feature. a nice example: http://stackoverflow.com/questions/27293960/swipe-to-dismiss-for-recyclerview – SacreDeveloper Sep 17 '15 at 10:06
  • 1
    @Kunu This is not what I am looking for. This library swipes complete row to the left and reveal the hidden view. However I want only part of the row to swipe. In the image above, only blue bubbles are swiped to the left and not the grey bubbles. Grey bubbles stay at their place. – Nitesh Kumar Sep 17 '15 at 11:24
  • @Kunu Please help me implement this. – Nitesh Kumar Sep 24 '15 at 10:07
  • @NiteshKhatri I haven't work on this kinda view. But have a look on [Inbox Style Swipe ListView](http://www.jayrambhia.com/blog/swipe-listview/). It may guide you for what you want to achieve. – Kunu Sep 24 '15 at 10:15
  • have you found a solution? – Andrey Solera Jul 13 '17 at 19:09
  • @Andrea I implemented this on my own. I didn't use any third party library. When I detect swipe on this list I animate blue bubbles of all the rows to the left. This is how I achieved it. – Nitesh Kumar Aug 26 '17 at 08:57
  • @NiteshKumar can you guide or share sample code of what you did to achieve this? – Kartik May 25 '20 at 11:14
  • Hi @NiteshKumar , could you please share your app reference where above implementation you did. – Sathish Gadde Mar 29 '21 at 12:46

0 Answers0