【问题标题】:How to use View.IOnTouchListener in a RecyclerView to swipe to reveal buttons如何在 RecyclerView 中使用 View.IOnTouchListener 滑动以显示按钮
【发布时间】:2019-12-23 17:04:43
【问题描述】:

我正在尝试将 Java 代码转换为 C# 以下是我目前所得到的:

MainActivity.cs

public class MainActivity : AppCompatActivity
{
    private RecyclerView recyclerViewOrders;
    private OrderAdapter recyclerViewAdapterOrders;
    private RecyclerView recyclerViewPackages;
    private PackageAdapter recyclerViewAdapterPackages;
    private RecyclerView.LayoutManager layoutManagerOrders;
    private RecyclerView.LayoutManager layoutManagerPackages;
    private List<OrderData> ordersList = new List<OrderData>();

    private List<PackageData> packageList = new List<PackageData>();

    SwipeToDeleteCallback swipeToDeleteCallback = null;

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.activity_main);

        recyclerViewOrders = (RecyclerView)FindViewById(Resource.Id.RecyclerViewOrders);
        recyclerViewPackages = (RecyclerView)FindViewById(Resource.Id.RecyclerViewPackages);
        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        recyclerViewOrders.HasFixedSize = true;
        recyclerViewPackages.HasFixedSize = true;
        // use a linear layout manager
        layoutManagerOrders = new LinearLayoutManager(this);
        recyclerViewOrders.SetLayoutManager(layoutManagerOrders);

        layoutManagerPackages = new LinearLayoutManager(this);
        recyclerViewPackages.SetLayoutManager(layoutManagerPackages);

        // specify an adapter (see also next example)
        //recyclerViewAdapter = new MyAdapter(myDataset);

        ordersList.Add(new OrderData { Order = "Order 1" });
        ordersList.Add(new OrderData { Order = "Order 2" });
        ordersList.Add(new OrderData { Order = "Order 3" });
        ordersList.Add(new OrderData { Order = "Order 4" });
        ordersList.Add(new OrderData { Order = "Order 5" });

        recyclerViewAdapterOrders = new OrderAdapter(ordersList);
        recyclerViewOrders.SetAdapter(recyclerViewAdapterOrders);

        packageList.Add(new PackageData { Package = "Package 1" });
        packageList.Add(new PackageData { Package = "Package 2" });
        packageList.Add(new PackageData { Package = "Package 3" });
        packageList.Add(new PackageData { Package = "Package 4" });
        packageList.Add(new PackageData { Package = "Package 5" });

        recyclerViewAdapterPackages = new PackageAdapter(packageList);
        recyclerViewPackages.SetAdapter(recyclerViewAdapterPackages);

        //SwipeToDeleteCallback swipeHandlerOrder = new SwipeToDeleteCallback(0, ItemTouchHelper.Left, this, recyclerViewAdapterOrders);

        SwipeToDeleteCallback swipeHandlerOrder = new SwipeToDeleteCallback(this, recyclerViewAdapterOrders);
        ItemTouchHelper itemTouchHelperOrder = new ItemTouchHelper(swipeHandlerOrder);
        itemTouchHelperOrder.AttachToRecyclerView(recyclerViewOrders);

        //SwipeToDeleteCallback swipeHandlerPackage = new SwipeToDeleteCallback(0, ItemTouchHelper.Left, this, recyclerViewAdapterPackages);
        //ItemTouchHelper itemTouchHelperPackage = new ItemTouchHelper(swipeHandlerPackage);
        //itemTouchHelperPackage.AttachToRecyclerView(recyclerViewPackages);
    }
    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
    {
        Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }

}

下面是我的 OrderAdapter.cs:

 // ADAPTER

// Adapter to connect the data set (orders) to the RecyclerView:
public class OrderAdapter : RecyclerView.Adapter
{
    // Event handler for item clicks:
    public event EventHandler<int> ItemClick;

    // Underlying data set (order):
    private List<OrderData> listOrderData; //= new List<OrderData>()

    // Load the adapter with the data set (order data) at construction time:
    public OrderAdapter(List<OrderData> orderData)
    {
        listOrderData = orderData;
    }

    // Create a new order CardView (invoked by the layout manager): 
    public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
    {
        // Inflate the CardView for the order:
        View itemView = LayoutInflater.From(parent.Context).
                    Inflate(Resource.Layout.OrdersCardView, parent, false);

        // Create a ViewHolder to find and hold these view references, and 
        // register OnClick with the view holder:
        return new OrderViewHolder(itemView, OnClick);
    }

    // Fill in the contents of the order list (invoked by the layout manager):
    public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
    {
        OrderViewHolder vh = holder as OrderViewHolder;

        // Set the TextView in this ViewHolder's CardView 
        // from this position in the order list:
        vh.Order.SetText(listOrderData[position].Order, null);
    }

    // Return the number of orders available in the order list:
    public override int ItemCount
    {
        get { return listOrderData.Count; }
    }

    // Raise an event when the item-click takes place:
    void OnClick(int position) { ItemClick?.Invoke(this, position); }

    public void RemoveItem(int position)
    {
        listOrderData.RemoveAt(position);
        NotifyItemChanged(position);
        NotifyItemRemoved(position);
        NotifyDataSetChanged();
    }
}

请看下面我的 SwipeToDeleteCallback.cs:

enum ButtonsState
{
    GONE,
    LEFT_VISIBLE,
    RIGHT_VISIBLE
}
public class SwipeToDeleteCallback : ItemTouchHelper.SimpleCallback, View.IOnTouchListener
{
    private OrderAdapter orderAdapter;
    private Context context;

    public static bool swipeBack = false;
    private ButtonsState buttonShowedState = ButtonsState.GONE;
    private RectF buttonInstance = null;
    private RecyclerView.ViewHolder currentItemViewHolder = null;
    private SwipeControllerActions buttonsActions = null;
    private static float buttonWidth = 300;

    public SwipeToDeleteCallback(SwipeControllerActions buttonsActions) : base(ItemTouchHelper.ActionStateIdle, ItemTouchHelper.Down)
    {
        this.buttonsActions = buttonsActions;
    }
    public SwipeToDeleteCallback(Context context, OrderAdapter orderAdapter) : base(ItemTouchHelper.ActionStateIdle, ItemTouchHelper.Down)
    {
        this.context = context;
        this.orderAdapter = orderAdapter;
    }
    public override int GetMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { return MakeMovementFlags(0, ItemTouchHelper.Left); }
    public override bool OnMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { return false; }

    public override void OnSwiped(RecyclerView.ViewHolder viewHolder, int direction) { }

    public override int ConvertToAbsoluteDirection(int flags, int layoutDirection)
    {
        if (swipeBack)
        {
            swipeBack = buttonShowedState != ButtonsState.GONE;
            return 0;
        }
        return base.ConvertToAbsoluteDirection(flags, layoutDirection);
    }

    public override void OnChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, bool isCurrentlyActive)
    {
        if (actionState == ItemTouchHelper.ActionStateSwipe)
        {
            if (buttonShowedState != ButtonsState.GONE)
            {
                if (buttonShowedState == ButtonsState.LEFT_VISIBLE) dX = Math.Max(dX, buttonWidth);
                if (buttonShowedState == ButtonsState.RIGHT_VISIBLE) dX = Math.Min(dX, -buttonWidth);
                base.OnChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            }
            else
            {
                SetTouchListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            }
        }

        if (buttonShowedState == ButtonsState.GONE)
        {
            base.OnChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        }
        currentItemViewHolder = viewHolder;
    }
    private void SetTouchListener(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, bool isCurrentlyActive)
    {
        recyclerView.SetOnTouchListener(this);


        if (swipeBack)
        {
            if (dX < -buttonWidth) { buttonShowedState = ButtonsState.RIGHT_VISIBLE; }
            if (dX > buttonWidth) { buttonShowedState = ButtonsState.LEFT_VISIBLE; }

            if (buttonShowedState != ButtonsState.GONE)
            {
                SetTouchDownListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
                SetItemsClickable(recyclerView, false);
            }
        }
    }

    private void SetTouchDownListener( Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, bool isCurrentlyActive )
    {
        recyclerView.SetOnTouchListener(this);

        SetTouchUpListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }

    private void SetTouchUpListener( Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, bool isCurrentlyActive )
    {
       base.OnChildDraw(c, recyclerView, viewHolder, 0F, dY, actionState, isCurrentlyActive);

        recyclerView.SetOnTouchListener(this);


        SetItemsClickable(recyclerView, true);

        swipeBack = false;

        if (buttonShowedState == ButtonsState.LEFT_VISIBLE)
        {
            buttonsActions.OnLeftClicked(viewHolder.AdapterPosition);
        }
        else if (buttonShowedState == ButtonsState.RIGHT_VISIBLE)
        {
            buttonsActions.OnRightClicked(viewHolder.AdapterPosition);
        }

        buttonShowedState = ButtonsState.GONE;
        currentItemViewHolder = null;
    }

    private void SetItemsClickable(RecyclerView recyclerView, bool isClickable)
    {
        for (int i = 0; i < recyclerView.ChildCount; ++i)
        {
            recyclerView.GetChildAt(i).Clickable = isClickable;
        }
    }

    private void DrawButtons(Canvas c, RecyclerView.ViewHolder viewHolder)
    {
        float buttonWidthWithoutPadding = buttonWidth - 20;
        float corners = 16;

        View itemView = viewHolder.ItemView;
        Paint p = new Paint();

        RectF leftButton = new RectF(itemView.Left, itemView.Top, itemView.Left + buttonWidthWithoutPadding, itemView.Bottom);
        p.Color = Color.Blue;
        c.DrawRoundRect(leftButton, corners, corners, p);
        DrawText("EDIT", c, leftButton, p);

        RectF rightButton = new RectF(itemView.Right - buttonWidthWithoutPadding, itemView.Top, itemView.Right, itemView.Bottom);
        p.Color = Color.Red;
        c.DrawRoundRect(rightButton, corners, corners, p);
        DrawText("DELETE", c, rightButton, p);

        buttonInstance = null;
        if (buttonShowedState == ButtonsState.LEFT_VISIBLE)
        {
            buttonInstance = leftButton;
        }
        else if (buttonShowedState == ButtonsState.RIGHT_VISIBLE)
        {
            buttonInstance = rightButton;
        }
    }

    private void DrawText(String text, Canvas c, RectF button, Paint p)
    {
        float textSize = 60;
        p.Color = Color.White;
        p.AntiAlias = true;
        p.TextSize = textSize;

        float textWidth = p.MeasureText(text);
        c.DrawText(text, button.CenterX() - (textWidth / 2), button.CenterY() + (textSize / 2), p);
    }

    public void OnDraw(Canvas c)
    {
        if (currentItemViewHolder != null)
        {
            DrawButtons(c, currentItemViewHolder);
        }
    }

    public bool OnTouch(View v, MotionEvent ev)
    {
        swipeBack = ev.Action == MotionEventActions.Cancel || ev.Action == MotionEventActions.Up;

        if (ev.Action == MotionEventActions.Down)
        {
            return true;
        }

        if (ev.Action == MotionEventActions.Up)
        {
            if (buttonsActions != null && buttonInstance != null && buttonInstance.Contains(ev.GetX(), ev.GetY()))
            {
                return true;
            }

            return false;
        }
        return false;
    }
}

最后是我的 SwipeControllerActions.cs:

 public abstract class SwipeControllerActions
{
    public void OnLeftClicked(int position) { }

    public void OnRightClicked(int position) { }

}

因为 C# 不使用匿名类,所以我在实现 OnTouch 时遇到了困难。 我将在下面发布 Java 中的原始代码:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

private PlayersDataAdapter mAdapter;
SwipeController swipeController = null;

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

    setPlayersDataAdapter();
    setupRecyclerView();
}

private void setPlayersDataAdapter() {
    List<Player> players = new ArrayList<>();
    try {
        InputStreamReader is = new InputStreamReader(getAssets().open("players.csv"));

        BufferedReader reader = new BufferedReader(is);
        reader.readLine();
        String line;
        String[] st;
        while ((line = reader.readLine()) != null) {
            st = line.split(",");
            Player player = new Player();
            player.setName(st[0]);
            player.setNationality(st[1]);
            player.setClub(st[4]);
            player.setRating(Integer.parseInt(st[9]));
            player.setAge(Integer.parseInt(st[14]));
            players.add(player);
        }
    } catch (IOException e) {

    }

    mAdapter = new PlayersDataAdapter(players);
}

private void setupRecyclerView() {
    RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recyclerView);

    recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
    recyclerView.setAdapter(mAdapter);

    swipeController = new SwipeController(new SwipeControllerActions() {
        @Override
        public void onRightClicked(int position) {
            mAdapter.players.remove(position);
            mAdapter.notifyItemRemoved(position);
            mAdapter.notifyItemRangeChanged(position, mAdapter.getItemCount());
        }
    });

    ItemTouchHelper itemTouchhelper = new ItemTouchHelper(swipeController);
    itemTouchhelper.attachToRecyclerView(recyclerView);

    recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            swipeController.onDraw(c);
        }
    });
}

}

SwipeController.java:

enum ButtonsState {
GONE,
LEFT_VISIBLE,
RIGHT_VISIBLE

}

类 SwipeController 扩展回调 {

private boolean swipeBack = false;

private ButtonsState buttonShowedState = ButtonsState.GONE;

private RectF buttonInstance = null;

private RecyclerView.ViewHolder currentItemViewHolder = null;

private SwipeControllerActions buttonsActions = null;

private static final float buttonWidth = 300;

public SwipeController(SwipeControllerActions buttonsActions) {
    this.buttonsActions = buttonsActions;
}

@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    return makeMovementFlags(0, LEFT | RIGHT);
}

@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
    return false;
}

@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {

}

@Override
public int convertToAbsoluteDirection(int flags, int layoutDirection) {
    if (swipeBack) {
        swipeBack = buttonShowedState != ButtonsState.GONE;
        return 0;
    }
    return super.convertToAbsoluteDirection(flags, layoutDirection);
}

@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
    if (actionState == ACTION_STATE_SWIPE) {
        if (buttonShowedState != ButtonsState.GONE) {
            if (buttonShowedState == ButtonsState.LEFT_VISIBLE) dX = Math.max(dX, buttonWidth);
            if (buttonShowedState == ButtonsState.RIGHT_VISIBLE) dX = Math.min(dX, -buttonWidth);
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        }
        else {
            setTouchListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        }
    }

    if (buttonShowedState == ButtonsState.GONE) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }
    currentItemViewHolder = viewHolder;
}

private void setTouchListener(final Canvas c, final RecyclerView recyclerView, final RecyclerView.ViewHolder viewHolder, final float dX, final float dY, final int actionState, final boolean isCurrentlyActive) {
    recyclerView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            swipeBack = event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP;
            if (swipeBack) {
                if (dX < -buttonWidth) buttonShowedState = ButtonsState.RIGHT_VISIBLE;
                else if (dX > buttonWidth) buttonShowedState  = ButtonsState.LEFT_VISIBLE;

                if (buttonShowedState != ButtonsState.GONE) {
                    setTouchDownListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
                    setItemsClickable(recyclerView, false);
                }
            }
            return false;
        }
    });
}

private void setTouchDownListener(final Canvas c, final RecyclerView recyclerView, final RecyclerView.ViewHolder viewHolder, final float dX, final float dY, final int actionState, final boolean isCurrentlyActive) {
    recyclerView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                setTouchUpListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            }
            return false;
        }
    });
}

private void setTouchUpListener(final Canvas c, final RecyclerView recyclerView, final RecyclerView.ViewHolder viewHolder, final float dX, final float dY, final int actionState, final boolean isCurrentlyActive) {
    recyclerView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_UP) {
                SwipeController.super.onChildDraw(c, recyclerView, viewHolder, 0F, dY, actionState, isCurrentlyActive);
                recyclerView.setOnTouchListener(new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        return false;
                    }
                });
                setItemsClickable(recyclerView, true);
                swipeBack = false;

                if (buttonsActions != null && buttonInstance != null && buttonInstance.contains(event.getX(), event.getY())) {
                    if (buttonShowedState == ButtonsState.LEFT_VISIBLE) {
                        buttonsActions.onLeftClicked(viewHolder.getAdapterPosition());
                    }
                    else if (buttonShowedState == ButtonsState.RIGHT_VISIBLE) {
                        buttonsActions.onRightClicked(viewHolder.getAdapterPosition());
                    }
                }
                buttonShowedState = ButtonsState.GONE;
                currentItemViewHolder = null;
            }
            return false;
        }
    });
}

private void setItemsClickable(RecyclerView recyclerView, boolean isClickable) {
    for (int i = 0; i < recyclerView.getChildCount(); ++i) {
        recyclerView.getChildAt(i).setClickable(isClickable);
    }
}

private void drawButtons(Canvas c, RecyclerView.ViewHolder viewHolder) {
    float buttonWidthWithoutPadding = buttonWidth - 20;
    float corners = 16;

    View itemView = viewHolder.itemView;
    Paint p = new Paint();

    RectF leftButton = new RectF(itemView.getLeft(), itemView.getTop(), itemView.getLeft() + buttonWidthWithoutPadding, itemView.getBottom());
    p.setColor(Color.BLUE);
    c.drawRoundRect(leftButton, corners, corners, p);
    drawText("EDIT", c, leftButton, p);

    RectF rightButton = new RectF(itemView.getRight() - buttonWidthWithoutPadding, itemView.getTop(), itemView.getRight(), itemView.getBottom());
    p.setColor(Color.RED);
    c.drawRoundRect(rightButton, corners, corners, p);
    drawText("DELETE", c, rightButton, p);

    buttonInstance = null;
    if (buttonShowedState == ButtonsState.LEFT_VISIBLE) {
        buttonInstance = leftButton;
    }
    else if (buttonShowedState == ButtonsState.RIGHT_VISIBLE) {
        buttonInstance = rightButton;
    }
}

private void drawText(String text, Canvas c, RectF button, Paint p) {
    float textSize = 60;
    p.setColor(Color.WHITE);
    p.setAntiAlias(true);
    p.setTextSize(textSize);

    float textWidth = p.measureText(text);
    c.drawText(text, button.centerX()-(textWidth/2), button.centerY()+(textSize/2), p);
}

public void onDraw(Canvas c) {
    if (currentItemViewHolder != null) {
        drawButtons(c, currentItemViewHolder);
    }
}

}

最后是 SwipeControllerActions.java:

public abstract class SwipeControllerActions {

public void onLeftClicked(int position) {}

public void onRightClicked(int position) {}

}

我可以让项目滑动,但无法显示文本或按钮区域。 任何帮助将不胜感激。

【问题讨论】:

    标签: java c# android-recyclerview ontouch


    【解决方案1】:

    你需要在SwipeToDeleteCallback.cs 的 OnChildDraw 结束时调用 DrawButtons。我也在处理这个问题,我做了一些小改动,但应该是这样的:

        if (buttonShowedState == ButtonsState.GONE)
                {
                   base.OnChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
                }
                currentItemViewHolder = viewHolder;
    
                DrawButtons(c, viewHolder);
            }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-16
      • 2017-12-11
      • 1970-01-01
      • 2014-05-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多