2424import androidx .recyclerview .widget .RecyclerView .ViewHolder ;
2525import androidx .recyclerview .widget .RecyclerViewAccessibilityDelegate ;
2626import androidx .recyclerview .widget .ItemTouchHelper ;
27+ import android .view .KeyEvent ;
2728import android .view .LayoutInflater ;
2829import android .view .MotionEvent ;
2930import android .view .View ;
@@ -93,10 +94,10 @@ public void onInitializeAccessibilityNodeInfo(View host,
9394 public boolean performAccessibilityAction (View host , int action , Bundle args ) {
9495 int fromPosition = recyclerView .getChildLayoutPosition (host );
9596 if (action == R .id .move_card_down_action ) {
96- swapCards (fromPosition , fromPosition + 1 , cardAdapter );
97+ cardAdapter . swapCards (fromPosition , fromPosition + 1 );
9798 return true ;
9899 } else if (action == R .id .move_card_up_action ) {
99- swapCards (fromPosition , fromPosition - 1 , cardAdapter );
100+ cardAdapter . swapCards (fromPosition , fromPosition - 1 );
100101 return true ;
101102 }
102103
@@ -118,10 +119,13 @@ private static int[] generateCardNumbers() {
118119 return cardNumbers ;
119120 }
120121
121- private static class CardAdapter extends RecyclerView .Adapter <RecyclerView .ViewHolder > {
122+ private static class CardAdapter extends RecyclerView .Adapter <RecyclerView .ViewHolder >
123+ implements OnKeyboardDragListener {
122124
123125 private final int [] cardNumbers ;
124126
127+ @ Nullable private ViewHolder draggedViewHolder ;
128+
125129 private ItemTouchHelper itemTouchHelper ;
126130
127131 private CardAdapter (int [] cardNumbers ) {
@@ -133,12 +137,12 @@ private CardAdapter(int[] cardNumbers) {
133137 public RecyclerView .ViewHolder onCreateViewHolder (@ NonNull ViewGroup parent , int viewType ) {
134138 LayoutInflater inflater = LayoutInflater .from (parent .getContext ());
135139 View view = inflater .inflate (R .layout .cat_card_list_item , parent , /* attachToRoot= */ false );
136- return new CardViewHolder (view );
140+ return new CardViewHolder (view , this );
137141 }
138142
139143 @ Override
140144 public void onBindViewHolder (@ NonNull RecyclerView .ViewHolder viewHolder , int position ) {
141- ((CardViewHolder ) viewHolder ).bind (cardNumbers [position ], itemTouchHelper );
145+ ((CardViewHolder ) viewHolder ).bind (cardNumbers [position ]);
142146 }
143147
144148 @ Override
@@ -150,27 +154,113 @@ private void setItemTouchHelper(ItemTouchHelper itemTouchHelper) {
150154 this .itemTouchHelper = itemTouchHelper ;
151155 }
152156
153- private static class CardViewHolder extends RecyclerView .ViewHolder {
157+ void swapCards (int fromPosition , int toPosition ) {
158+ if (fromPosition < 0
159+ || fromPosition >= cardNumbers .length
160+ || toPosition < 0
161+ || toPosition >= cardNumbers .length ) {
162+ return ;
163+ }
164+
165+ int fromNumber = cardNumbers [fromPosition ];
166+ cardNumbers [fromPosition ] = cardNumbers [toPosition ];
167+ cardNumbers [toPosition ] = fromNumber ;
168+ notifyItemMoved (fromPosition , toPosition );
169+ }
170+
171+ void cancelDrag () {
172+ if (draggedViewHolder != null ) {
173+ ((MaterialCardView ) draggedViewHolder .itemView ).setDragged (false );
174+ draggedViewHolder = null ;
175+ }
176+ }
177+
178+ @ Override
179+ public void onDragStarted (@ NonNull ViewHolder viewHolder ) {
180+ itemTouchHelper .startDrag (viewHolder );
181+ }
182+
183+ @ Override
184+ public void onKeyboardDragToggle (@ NonNull ViewHolder viewHolder ) {
185+ boolean isCurrentlyDragged = draggedViewHolder == viewHolder ;
186+ cancelDrag ();
187+ if (!isCurrentlyDragged ) {
188+ draggedViewHolder = viewHolder ;
189+ ((MaterialCardView ) viewHolder .itemView ).setDragged (true );
190+ }
191+ }
192+
193+ @ Override
194+ public boolean onKeyboardMoved (int keyCode ) {
195+ if (draggedViewHolder == null ) {
196+ return false ;
197+ }
198+
199+ int fromPosition = draggedViewHolder .getBindingAdapterPosition ();
200+ if (fromPosition == RecyclerView .NO_POSITION ) {
201+ return false ;
202+ }
203+
204+ switch (keyCode ) {
205+ case KeyEvent .KEYCODE_DPAD_UP :
206+ swapCards (fromPosition , fromPosition - 1 );
207+ return true ;
208+ case KeyEvent .KEYCODE_DPAD_DOWN :
209+ swapCards (fromPosition , fromPosition + 1 );
210+ return true ;
211+ default :
212+ return false ;
213+ }
214+ }
215+
216+ private static class CardViewHolder extends RecyclerView .ViewHolder {
154217
155218 private final TextView titleView ;
156- private final View dragHandleView ;
157219
158- private CardViewHolder (View itemView ) {
220+ private CardViewHolder (View itemView , OnKeyboardDragListener listener ) {
159221 super (itemView );
160- titleView = itemView .findViewById (R .id .cat_card_list_item_title );
161- dragHandleView = itemView .findViewById (R .id .cat_card_list_item_drag_handle );
162- }
163222
164- private void bind (int cardNumber , final ItemTouchHelper itemTouchHelper ) {
165- titleView .setText (String .format (Locale .getDefault (), "Card #%02d" , cardNumber ));
223+ MaterialCardView cardView = (MaterialCardView ) itemView ;
224+ cardView .setFocusable (true );
225+ cardView .setOnKeyListener (
226+ (v , keyCode , event ) -> {
227+ if (event .getAction () != KeyEvent .ACTION_DOWN ) {
228+ return false ;
229+ }
230+
231+ switch (keyCode ) {
232+ case KeyEvent .KEYCODE_ENTER :
233+ case KeyEvent .KEYCODE_DPAD_CENTER :
234+ listener .onKeyboardDragToggle (this );
235+ return true ;
236+ case KeyEvent .KEYCODE_DPAD_UP :
237+ case KeyEvent .KEYCODE_DPAD_DOWN :
238+ return listener .onKeyboardMoved (keyCode );
239+ default :
240+ return false ;
241+ }
242+ });
243+
244+ View dragHandleView = itemView .findViewById (R .id .cat_card_list_item_drag_handle );
166245 dragHandleView .setOnTouchListener (
167246 (v , event ) -> {
168- if (event .getAction () == MotionEvent .ACTION_DOWN ) {
169- itemTouchHelper .startDrag (CardViewHolder .this );
170- return true ;
247+ switch (event .getAction ()) {
248+ case MotionEvent .ACTION_DOWN :
249+ listener .onDragStarted (this );
250+ return true ;
251+ case MotionEvent .ACTION_UP :
252+ v .performClick ();
253+ break ;
254+ default : // fall out
171255 }
172256 return false ;
173257 });
258+
259+ titleView = itemView .findViewById (R .id .cat_card_list_item_title );
260+ }
261+
262+ private void bind (int cardNumber ) {
263+ titleView .setText (String .format (Locale .getDefault (), "Card #%02d" , cardNumber ));
174264 }
175265 }
176266 }
@@ -199,10 +289,9 @@ public boolean onMove(
199289 @ NonNull RecyclerView recyclerView ,
200290 @ NonNull ViewHolder viewHolder ,
201291 @ NonNull ViewHolder target ) {
202- int fromPosition = viewHolder .getAdapterPosition ();
203- int toPosition = target .getAdapterPosition ();
204-
205- swapCards (fromPosition , toPosition , cardAdapter );
292+ int fromPosition = viewHolder .getBindingAdapterPosition ();
293+ int toPosition = target .getBindingAdapterPosition ();
294+ cardAdapter .swapCards (fromPosition , toPosition );
206295 return true ;
207296 }
208297
@@ -214,6 +303,7 @@ public void onSelectedChanged(@Nullable ViewHolder viewHolder, int actionState)
214303 super .onSelectedChanged (viewHolder , actionState );
215304
216305 if (actionState == ItemTouchHelper .ACTION_STATE_DRAG && viewHolder != null ) {
306+ cardAdapter .cancelDrag ();
217307 dragCardView = (MaterialCardView ) viewHolder .itemView ;
218308 dragCardView .setDragged (true );
219309 } else if (actionState == ItemTouchHelper .ACTION_STATE_IDLE && dragCardView != null ) {
@@ -223,10 +313,11 @@ public void onSelectedChanged(@Nullable ViewHolder viewHolder, int actionState)
223313 }
224314 }
225315
226- private static void swapCards (int fromPosition , int toPosition , CardAdapter cardAdapter ) {
227- int fromNumber = cardAdapter .cardNumbers [fromPosition ];
228- cardAdapter .cardNumbers [fromPosition ] = cardAdapter .cardNumbers [toPosition ];
229- cardAdapter .cardNumbers [toPosition ] = fromNumber ;
230- cardAdapter .notifyItemMoved (fromPosition , toPosition );
316+ private interface OnKeyboardDragListener {
317+ void onDragStarted (@ NonNull ViewHolder viewHolder );
318+
319+ void onKeyboardDragToggle (@ NonNull ViewHolder viewHolder );
320+
321+ boolean onKeyboardMoved (int keyCode );
231322 }
232323}
0 commit comments