dhiku dhiku - 1 month ago 4
Android Question

Swipe right display button in android custom list view

I am trying to implement, show the button on swipe right in the custom list view. Problem i am facing right now is, i implemented the onSwipeTouchListener which i found online. I could able to see the swipe button. But its not consistent, i mean if i swipe right on one row, button is displaying in some other row. This is what i tried.

i created an adapter for holding the view

//TransactionAddDropViewHolder


public static final class TransactionAddDropViewHolder {

public View moveUpButton = null;
public View moveDownButton = null;
public View withdrawButton = null;
public View reviewButton = null;

public View approveButton = null;
public View rejectButton = null;

public LinearLayout addContainer = null;
public LinearLayout dropContainer = null;



public void swipeButtons() {
addDropListView.setOnTouchListener(new OnSwipeTouchListener() {

public void onSwipeRight() {
withdrawButton.setVisibility(View.VISIBLE);
}

public void onSwipeLeft() {
withdrawButton.setVisibility(View.INVISIBLE);
}
});
}

public void showUserButtons() {
this.moveUpButton.setVisibility(View.VISIBLE);
this.moveDownButton.setVisibility(View.VISIBLE);
// this.withdrawButton.setVisibility(View.VISIBLE);
}

public void hideUserButtons() {
this.moveUpButton.setVisibility(View.GONE);
this.moveDownButton.setVisibility(View.GONE);
// this.withdrawButton.setVisibility(View.GONE);
}

}


And this is my Custom View where i am displaying the onSwipeButton

@Override
public View getView(final int position, View convertView,
ViewGroup parent) {

final TransactionAddDrop addDropData = this.addDropList.get(position);

TransactionAddDropViewHolder holder = null;

if (convertView == null) {
convertView = inflater.inflate(R.layout.fragment_pending_transaction_list_item, null);
holder = new TransactionAddDropViewHolder();

holder.withdrawButton = convertView.findViewById(R.id.pendingTransactionItem_withdrawButton);
holder.addContainer = (LinearLayout) convertView.findViewById(R.id.pendingTransactionItem_addContainer);
**holder.swipeButtons();**
convertView.setTag(holder);
} else {
holder = (TransactionAddDropViewHolder) convertView.getTag();
holder.swipeButtons();
}


And this is my OnSwipeTouchListener Class

private final class GestureListener extends SimpleOnGestureListener {

private static final int SWIPE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;

@Override
public boolean onDown(MotionEvent e) {
return true;
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
boolean result = false;
try {
float diffY = e2.getY() - e1.getY();
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
onSwipeRight();
} else {
onSwipeLeft();
}
}
} else {
if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
onSwipeBottom();
} else {
onSwipeTop();
}
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
return result;
}
}


Adding the XML Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.cbssports.nflapp.ffb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/swipeRight"
android:orientation="vertical" >

<LinearLayout
android:id="@+id/claimlayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<LinearLayout
android:id="@+id/claim"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp" >

<com.cbssports.nflapp.ffb.CustomText
android:id="@+id/claimNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="1"
android:textColor="#B4B4B5"
android:textSize="36dp"
app:typeface="oswald_regular" />
</LinearLayout>

<RelativeLayout
android:id="@+id/pendingPlayers"
android:layout_width="200dp"
android:layout_height="wrap_content" >

<LinearLayout
android:id="@+id/tradeDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:orientation="horizontal"
android:layout_toRightOf="@+id/claimlayout"
android:paddingTop="5dp" >

<com.cbssports.nflapp.ffb.CustomText
android:id="@+id/pendingTransactionItem_teamName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#B4B4B5"
android:textSize="9dp"
app:typeface="oswald_regular" />

<com.cbssports.nflapp.ffb.CustomText
android:id="@+id/pendingTransactionItem_bidAmount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#797979"
android:text=""
android:textColor="#ffffff"
android:textSize="9dp" />
</LinearLayout>

<LinearLayout
android:id="@+id/adddropView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/tradeDate"
android:layout_toRightOf="@+id/claimlayout"
android:orientation="vertical" >

<LinearLayout
android:id="@+id/addViewContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<com.cbssports.nflapp.ffb.CustomText
android:layout_width="50dp"
android:layout_height="wrap_content"
android:paddingRight="10dp"
android:text="ADD"
android:textColor="#797979"
android:textSize="15dp"
app:typeface="oswald_regular" />

<LinearLayout
android:id="@+id/pendingTransactionItem_addContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" />
</LinearLayout>

<LinearLayout
android:id="@+id/adddropViewContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/claimlayout"
android:orientation="horizontal" >

<com.cbssports.nflapp.ffb.CustomText
android:layout_width="50dp"
android:layout_height="wrap_content"
android:paddingRight="10dp"
android:text="DROP"
android:textColor="#797979"
android:textSize="15dp"
app:typeface="oswald_regular" />

<LinearLayout
android:id="@+id/pendingTransactionItem_dropContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>

<LinearLayout
android:id="@+id/pendingTransactionItem_userButtonContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="center_vertical"
android:layout_toRightOf="@+id/pendingPlayers"
android:gravity="center"
android:orientation="vertical" >

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="center_vertical"
android:gravity="center"
android:orientation="horizontal" >

<Button
android:id="@+id/pendingTransactionItem_moveUpButton"
android:layout_width="20dp"
android:layout_height="15dp"
android:layout_margin="3dp"
android:background="@drawable/gfx_up_arrow"
android:padding="1dp"
android:textColor="#ffffff"
android:textSize="7sp"
android:visibility="visible" />

<Button
android:id="@+id/pendingTransactionItem_moveDownButton"
android:layout_width="20dp"
android:layout_height="15dp"
android:layout_margin="3dp"
android:background="@drawable/gfx_down_arrow"
android:padding="1dp"
android:textColor="#ffffff"
android:textSize="7sp"
android:visibility="visible" />
</LinearLayout>

<ImageView
android:id="@+id/pendingTransactionItem_withdrawButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="1dp"
android:src="@drawable/ic_cancelclaim"
android:visibility="gone" />

<!-- Transaction Review -->

<ImageView
android:id="@+id/pendingTransactionItem_reviewButton"
android:layout_width="38dp"
android:layout_height="38dp"
android:padding="1dp"
android:src="@drawable/btn_review"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>




Adding one more question. getting the total Count of the view. But want to display in the different layout. Here is my onCreatedView

public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {

fragmentPendingTrades = inflater.inflate(R.layout.fragment_transactions_pending, container, false);
pendingTradesView = inflater;

return fragmentPendingTrades;
}

public void onViewCreated(final View view, final Bundle savedInstanceState) {

this.addDropListView = (ListView) view.findViewById(R.id.transactions_pending_transactionsListView);
this.addDropAdapter = new TransactionAddDropAdapter(pendingTradesView);
this.addDropListView.setAdapter(this.addDropAdapter);
this.emptyTransationsContainer = view.findViewById(R.id.transactions_pending_transactions_emptyContainer);



TextView getTotalCount = (TextView) view.findViewById(R.id.transactions_pending_TransactionsAddDropCount);

getTotalCount.setText(""+addDropListView.getCount());



}

Answer

So, this can be done 2 ways as I see it:

  1. Listen to swipes on the entire list, figure out which row the swiped occurred on, and show/hide the button for the correct row.
  2. Listen to swipes on each row, and show/hide the button for that row.

Right now you are setting the listener on the list, but trying to apply show/hide the button attached in the adapter. In a way, you are doing half of each solution. With what you have, I suggest you go with option #2.

In your TransactionAddDropViewHolder, you need to get a reference to the root View in the row layout, so we can set the OnSwipeTouchListener to it.

public static final class TransactionAddDropViewHolder {

    public View rootView = null;

    // Your other code

    public void swipeButtons() {
        rootView.setOnTouchListener(new OnSwipeTouchListener() {

            public void onSwipeRight() {
                withdrawButton.setVisibility(View.VISIBLE);
            }

            public void onSwipeLeft() {
                withdrawButton.setVisibility(View.INVISIBLE);
            }
        });
    }

    // Your other code

}

Next, in your getView method, update it so your TransactionAddDropViewHolder gets a reference to the root View in the layout.

@Override
public View getView(final int position, View convertView, ViewGroup parent) {

    final TransactionAddDrop addDropData = this.addDropList.get(position);

    TransactionAddDropViewHolder holder = null;

    if (convertView == null) {
        convertView = inflater.inflate(R.layout.fragment_pending_transaction_list_item, null);
        holder = new TransactionAddDropViewHolder();

        // Get a reference to the root of the row layout
        holder.rootView = convertView.findViewById(R.id.swipeRight); 

        holder.withdrawButton = convertView.findViewById(R.id.pendingTransactionItem_withdrawButton);
        holder.addContainer = (LinearLayout) convertView.findViewById(R.id.pendingTransactionItem_addContainer);

        holder.swipeButtons();
        convertView.setTag(holder);
    } else {
        holder = (TransactionAddDropViewHolder) convertView.getTag();
        holder.swipeButtons(); 
    }
}

There is one more issue you will have, the button will correctly show up when swiped, but if the user continues to scroll, it will appear on other rows. This is because ListView recycles its row's Views. I suggest you do either 2 things:

  1. When the user starts scrolling again after the button is present, you hide it again. This is what iOS does with "swipe to delete" in lists.
  2. Use a boolean to store in your data object if the row is in the mode where the button is visible, isDeleteShowing for example. Then in your onSwipe callbacks, you set isDeleteShowing to true/false. Lastly, in your getView, you show/hide of the button there depending on isDeleteShowing for that data row.

Hope this all helps.