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.
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.
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.
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.