You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

点击键盘「下一步」时自定义RecyclerView中EditText焦点跳转顺序

Custom Focus Traversal for RecyclerView EditTexts (Xamarin.Android + Java)

Got it, let's solve this custom focus order problem for your RecyclerView items. The default "Next" button behavior jumps through EditTexts within the same item first, but we need to redefine that sequence to go through all Box1-Box3 across items first, then Box4-Box6.


Xamarin.Android (C#) Solution

First, let's set up our ViewHolder and adapter logic to handle the custom focus flow:

Step 1: Define the ViewHolder

Create a ViewHolder class to hold references to all 6 EditTexts in each item:

public class ItemViewHolder : RecyclerView.ViewHolder
{
    public EditText Box1 { get; private set; }
    public EditText Box2 { get; private set; }
    public EditText Box3 { get; private set; }
    public EditText Box4 { get; private set; }
    public EditText Box5 { get; private set; }
    public EditText Box6 { get; private set; }

    public ItemViewHolder(View itemView) : base(itemView)
    {
        Box1 = itemView.FindViewById<EditText>(Resource.Id.Box1);
        Box2 = itemView.FindViewById<EditText>(Resource.Id.Box2);
        Box3 = itemView.FindViewById<EditText>(Resource.Id.Box3);
        Box4 = itemView.FindViewById<EditText>(Resource.Id.Box4);
        Box5 = itemView.FindViewById<EditText>(Resource.Id.Box5);
        Box6 = itemView.FindViewById<EditText>(Resource.Id.Box6);
    }
}

Step 2: Bind Custom Focus Listeners

In your adapter's OnBindViewHolder method, attach an OnEditorActionListener to each EditText to handle the "Next" button press:

public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
    var viewHolder = holder as ItemViewHolder;
    if (viewHolder == null) return;

    // Assign listeners to each box with their position and index
    SetNextFocusListener(viewHolder.Box1, position, 0);
    SetNextFocusListener(viewHolder.Box2, position, 1);
    SetNextFocusListener(viewHolder.Box3, position, 2);
    SetNextFocusListener(viewHolder.Box4, position, 3);
    SetNextFocusListener(viewHolder.Box5, position, 4);
    SetNextFocusListener(viewHolder.Box6, position, 5);
}

private void SetNextFocusListener(EditText editText, int itemPosition, int boxIndex)
{
    editText.SetOnEditorActionListener((v, actionId, eventArgs) =>
    {
        if (actionId == ImeAction.Next)
        {
            NavigateToNextEditText(itemPosition, boxIndex, v);
            return true;
        }
        return false;
    });
}

Step 3: Implement Custom Navigation Logic

Add a method to calculate and focus the next EditText according to your desired sequence:

private void NavigateToNextEditText(int currentItemPos, int currentBoxIndex, View v)
{
    int totalItems = ItemCount;
    InputMethodManager imm = (InputMethodManager)Context.GetSystemService(Context.InputMethodService);

    // Handle Box1-Box3 group (indices 0,1,2)
    if (currentBoxIndex < 3)
    {
        if (currentBoxIndex < 2)
        {
            // Move to next box in the same item (Box1→Box2, Box2→Box3)
            var currentHolder = FindViewHolderForAdapterPosition(currentItemPos) as ItemViewHolder;
            EditText nextBox = currentBoxIndex switch
            {
                0 => currentHolder?.Box2,
                1 => currentHolder?.Box3,
                _ => null
            };
            nextBox?.RequestFocus();
            imm.ShowSoftInput(nextBox, ShowFlags.Forced);
        }
        else
        {
            // Box3: move to Box1 of next item, or Box4 of first item if last item
            if (currentItemPos < totalItems - 1)
            {
                int nextItemPos = currentItemPos + 1;
                RecyclerView.SmoothScrollToPosition(nextItemPos);
                RecyclerView.Post(() =>
                {
                    var nextHolder = FindViewHolderForAdapterPosition(nextItemPos) as ItemViewHolder;
                    nextHolder?.Box1?.RequestFocus();
                    imm.ShowSoftInput(nextHolder?.Box1, ShowFlags.Forced);
                });
            }
            else
            {
                // Last item's Box3: jump to first item's Box4
                RecyclerView.SmoothScrollToPosition(0);
                RecyclerView.Post(() =>
                {
                    var firstHolder = FindViewHolderForAdapterPosition(0) as ItemViewHolder;
                    firstHolder?.Box4?.RequestFocus();
                    imm.ShowSoftInput(firstHolder?.Box4, ShowFlags.Forced);
                });
            }
        }
    }
    // Handle Box4-Box6 group (indices 3,4,5)
    else
    {
        if (currentBoxIndex < 5)
        {
            // Move to next box in the same item (Box4→Box5, Box5→Box6)
            var currentHolder = FindViewHolderForAdapterPosition(currentItemPos) as ItemViewHolder;
            EditText nextBox = currentBoxIndex switch
            {
                3 => currentHolder?.Box5,
                4 => currentHolder?.Box6,
                _ => null
            };
            nextBox?.RequestFocus();
            imm.ShowSoftInput(nextBox, ShowFlags.Forced);
        }
        else
        {
            // Box6: move to Box4 of next item, or hide keyboard if last item
            if (currentItemPos < totalItems - 1)
            {
                int nextItemPos = currentItemPos + 1;
                RecyclerView.SmoothScrollToPosition(nextItemPos);
                RecyclerView.Post(() =>
                {
                    var nextHolder = FindViewHolderForAdapterPosition(nextItemPos) as ItemViewHolder;
                    nextHolder?.Box4?.RequestFocus();
                    imm.ShowSoftInput(nextHolder?.Box4, ShowFlags.Forced);
                });
            }
            else
            {
                // Last item's Box6: hide keyboard
                imm.HideSoftInputFromWindow(v.WindowToken, HideSoftInputFlags.None);
            }
        }
    }
}

Java Native Android Solution

The logic mirrors the Xamarin approach, just in Java:

Step 1: ViewHolder Class

public class ItemViewHolder extends RecyclerView.ViewHolder {
    public EditText Box1, Box2, Box3, Box4, Box5, Box6;

    public ItemViewHolder(View itemView) {
        super(itemView);
        Box1 = itemView.findViewById(R.id.Box1);
        Box2 = itemView.findViewById(R.id.Box2);
        Box3 = itemView.findViewById(R.id.Box3);
        Box4 = itemView.findViewById(R.id.Box4);
        Box5 = itemView.findViewById(R.id.Box5);
        Box6 = itemView.findViewById(R.id.Box6);
    }
}

Step 2: Bind Listeners in Adapter

Make sure your adapter holds a reference to the RecyclerView (pass it in the constructor):

public class ItemsAdapter extends RecyclerView.Adapter<ItemViewHolder> {
    private final RecyclerView recyclerView;
    // Other fields...

    public ItemsAdapter(RecyclerView recyclerView) {
        this.recyclerView = recyclerView;
    }

    @Override
    public void onBindViewHolder(@NonNull ItemViewHolder holder, int position) {
        setNextFocusListener(holder.Box1, position, 0);
        setNextFocusListener(holder.Box2, position, 1);
        setNextFocusListener(holder.Box3, position, 2);
        setNextFocusListener(holder.Box4, position, 3);
        setNextFocusListener(holder.Box5, position, 4);
        setNextFocusListener(holder.Box6, position, 5);
    }

    private void setNextFocusListener(EditText editText, int itemPosition, int boxIndex) {
        editText.setOnEditorActionListener((v, actionId, event) -> {
            if (actionId == EditorInfo.IME_ACTION_NEXT) {
                navigateToNextEditText(itemPosition, boxIndex, v);
                return true;
            }
            return false;
        });
    }
}

Step 3: Custom Navigation Logic

private void navigateToNextEditText(int currentItemPos, int currentBoxIndex, View v) {
    int totalItems = getItemCount();
    InputMethodManager imm = (InputMethodManager) recyclerView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);

    if (currentBoxIndex < 3) {
        if (currentBoxIndex < 2) {
            ItemViewHolder currentHolder = (ItemViewHolder) recyclerView.findViewHolderForAdapterPosition(currentItemPos);
            EditText nextBox = switch (currentBoxIndex) {
                case 0 -> currentHolder.Box2;
                case 1 -> currentHolder.Box3;
                default -> null;
            };
            if (nextBox != null) {
                nextBox.requestFocus();
                imm.showSoftInput(nextBox, InputMethodManager.SHOW_FORCED);
            }
        } else {
            if (currentItemPos < totalItems - 1) {
                int nextItemPos = currentItemPos + 1;
                recyclerView.smoothScrollToPosition(nextItemPos);
                recyclerView.post(() -> {
                    ItemViewHolder nextHolder = (ItemViewHolder) recyclerView.findViewHolderForAdapterPosition(nextItemPos);
                    if (nextHolder != null) {
                        nextHolder.Box1.requestFocus();
                        imm.showSoftInput(nextHolder.Box1, InputMethodManager.SHOW_FORCED);
                    }
                });
            } else {
                recyclerView.smoothScrollToPosition(0);
                recyclerView.post(() -> {
                    ItemViewHolder firstHolder = (ItemViewHolder) recyclerView.findViewHolderForAdapterPosition(0);
                    if (firstHolder != null) {
                        firstHolder.Box4.requestFocus();
                        imm.showSoftInput(firstHolder.Box4, InputMethodManager.SHOW_FORCED);
                    }
                });
            }
        }
    } else {
        if (currentBoxIndex < 5) {
            ItemViewHolder currentHolder = (ItemViewHolder) recyclerView.findViewHolderForAdapterPosition(currentItemPos);
            EditText nextBox = switch (currentBoxIndex) {
                case 3 -> currentHolder.Box5;
                case 4 -> currentHolder.Box6;
                default -> null;
            };
            if (nextBox != null) {
                nextBox.requestFocus();
                imm.showSoftInput(nextBox, InputMethodManager.SHOW_FORCED);
            }
        } else {
            if (currentItemPos < totalItems - 1) {
                int nextItemPos = currentItemPos + 1;
                recyclerView.smoothScrollToPosition(nextItemPos);
                recyclerView.post(() -> {
                    ItemViewHolder nextHolder = (ItemViewHolder) recyclerView.findViewHolderForAdapterPosition(nextItemPos);
                    if (nextHolder != null) {
                        nextHolder.Box4.requestFocus();
                        imm.showSoftInput(nextHolder.Box4, InputMethodManager.SHOW_FORCED);
                    }
                });
            } else {
                imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
            }
        }
    }
}

Key Notes

  • Scroll Handling: We use SmoothScrollToPosition and Post to ensure the target item is visible before requesting focus, since findViewHolderForAdapterPosition returns null for off-screen items.
  • Keyboard Control: We explicitly show/hide the keyboard to maintain consistency when jumping between items.

内容的提问来源于stack exchange,提问作者Vincent Elbert Budiman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:28:10