title: Android: RecyclerView, Drag-and-Drop (External View), and Auto-Scroll?
body: <p>Here is an image of what I am trying to do: </p>
  
  <p><a href="http://i.stack.imgur.com/vJUfH.png" rel="nofollow">Image:  Drag-and-drop app with a list and an item outside the list</a></p>
  
  <p>I am trying to make an explorer-like file browser, with drag-and-drop to move the files, but I run into a problem. </p>
  
  <p>I know there is a special RecyclerView drag-and-drop interface (there is <a href="https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd#.yt2erbko9" rel="nofollow">this</a>, for example), but I haven't been able to find examples that tell how to move things between the inside and <em>outside</em> of the list. </p>
  
  <p>Here is my XML: </p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;LinearLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical"
      tools:context="net.wbord.recyclerviewdragdropautoscrolltest.MainActivity"&gt;
  
      &lt;TextView
          android:id="@+id/exampleItem"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:textSize="20sp"
          android:text="Hello World!"/&gt;
  
      &lt;Space
          android:layout_width="match_parent"
          android:layout_height="50dp"
  
          /&gt;
  
  
      &lt;android.support.v7.widget.RecyclerView
          android:id="@+id/mainList"
          android:layout_width="match_parent"
          android:layout_height="0dp"
          android:layout_weight="1"
  
          android:paddingLeft="100dp"
          android:paddingRight="100dp"
          android:clipToPadding="false"
          /&gt;
  
  
  &lt;/LinearLayout&gt;
  </code></pre>
  
  <p>And the Java: </p>
  
  <pre><code>package net.wbord.recyclerviewdragdropautoscrolltest;
  
  import android.app.Activity;
  import android.content.ClipData;
  import android.content.ClipDescription;
  import android.os.Bundle;
  import android.support.v7.widget.LinearLayoutManager;
  import android.support.v7.widget.RecyclerView;
  import android.view.DragEvent;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.TextView;
  import android.widget.Toast;
  
  public class MainActivity extends Activity {
  
      @Override
      protected void onCreate (Bundle savedInstanceState) {
          super.onCreate (savedInstanceState);
          setContentView (R.layout.activity_main);
  
          // Find Views: 
          final TextView exampleItem = (TextView) findViewById (R.id.exampleItem); 
          final RecyclerView rv = (RecyclerView) findViewById (R.id.mainList); 
  
          // Define Drag Listener: 
          final View.OnDragListener onDrag = new View.OnDragListener () {
              @Override
              public boolean onDrag (View v, DragEvent event) {
                  switch (event.getAction ()) { 
                      case DragEvent.ACTION_DRAG_ENTERED: 
                          v.setScaleX (1.5f); v.setScaleY (1.5f); 
                          handleScroll (rv, v); 
                          break; 
                      case DragEvent.ACTION_DRAG_EXITED: 
                      case DragEvent.ACTION_DRAG_ENDED: 
                          v.setScaleX (1); v.setScaleY (1); 
                          break; 
                      case DragEvent.ACTION_DROP: 
                          ClipData data = event.getClipData (); 
                          String folder = (String) v.getTag (); 
                          String msg = "File '" + data.getItemAt (0).getText () + "' " + 
                                               "moved into folder '" + folder + "'"; 
                          Toast.makeText (MainActivity.this, msg, Toast.LENGTH_LONG).show (); 
                          break; 
                  } 
                  return true; 
              }
          }; 
  
          // The "file" for the user to drag-drop into a folder: 
          exampleItem.setOnLongClickListener (new View.OnLongClickListener () {
              @Override
              public boolean onLongClick (View v) {
                  // Start drag: 
                  ClipData.Item item = new ClipData.Item (exampleItem.getText ()); 
                  ClipData data = new ClipData (exampleItem.getText (), 
                                                       new String [] {ClipDescription.MIMETYPE_TEXT_PLAIN}, 
                                                       item); 
                  View.DragShadowBuilder builder = new View.DragShadowBuilder (exampleItem); 
                  v.startDrag (data, builder, null, 0); 
                  return true; 
              }
          }); 
  
          // The list of "folders" that can accept the file: 
          rv.setLayoutManager (new LinearLayoutManager (this, LinearLayoutManager.VERTICAL, false)); 
          rv.setAdapter (new RecyclerView.Adapter () {
              class ViewHolder extends RecyclerView.ViewHolder { 
                  private final TextView vItem; 
                  public ViewHolder (TextView textView) { 
                      super (textView); 
                      vItem = textView; 
                  } 
                  public void bind (String itemName) { 
                      vItem.setText (itemName); 
                      vItem.setTag (itemName); 
                      vItem.setOnDragListener (onDrag); 
                  } 
              } 
              @Override
              public RecyclerView.ViewHolder onCreateViewHolder (ViewGroup parent, int viewType) { 
                  TextView vText = new TextView (MainActivity.this); 
                  vText.setTextSize (50); 
                  return new ViewHolder (vText); 
              } 
              @Override
              public void onBindViewHolder (RecyclerView.ViewHolder holder, int position) {
                  if (holder instanceof ViewHolder) 
                      ((ViewHolder) holder).bind (getItem (position)); 
              }
              @Override
              public int getItemCount () {
                  return 100; 
              }
              public String getItem (int position) { 
                  return "Folder " + (1 + position); 
              } 
          });
      }
  
      protected void handleScroll (RecyclerView vList, View viewHoveredOver) { 
          LinearLayoutManager mgr = (LinearLayoutManager) vList.getLayoutManager (); 
          int iFirst = mgr.findFirstCompletelyVisibleItemPosition (); 
          int iLast = mgr.findLastCompletelyVisibleItemPosition (); 
          // Auto-Scroll: 
          if (mgr.findViewByPosition (iFirst) == viewHoveredOver) 
              vList.smoothScrollToPosition (Math.max (iFirst - 1, 0)); 
          else if (mgr.findViewByPosition (iLast) == viewHoveredOver) 
              vList.smoothScrollToPosition (Math.min (iLast + 1, 
                      mgr.getChildCount ())); 
      } 
  }
  </code></pre>
  
  <p>Basically every time a "folder" is ACTION_DRAG_ENTER-ed, the handleScroll () method is called: it checks which folder the dragged file is hovering over, and uses that to scroll the RecyclerView. </p>
  
  <p>The problem is the RecyclerView's recycling mechanism: as I understand it, the views in the recycling pool are not ACTION_DRAG_STARTED, so the views that are auto-scrolled into view are not capable of receiving the file, nor capable of auto-scrolling the list further. </p>
  
  <p>How does drag-and-drop work between a RecyclerView and its outside? With auto-scroll? </p>
  
  <p>Is there a way to add the new views to the drag even after the drag has been started? </p>
  
  <p>Thanks. </p>