点击键盘「下一步」时自定义RecyclerView中EditText焦点跳转顺序
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
SmoothScrollToPositionandPostto ensure the target item is visible before requesting focus, sincefindViewHolderForAdapterPositionreturns 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

