OSDN Git Service

Implement PgUp/PgDn support.
authorBen Kwa <kenobi@google.com>
Wed, 3 Feb 2016 07:00:02 +0000 (23:00 -0800)
committerBen Kwa <kenobi@google.com>
Mon, 8 Feb 2016 17:52:43 +0000 (09:52 -0800)
- Clean up our LayoutManager code.  We actually only need a single
  layout manager, since a GridLayoutManager is a LinearLayoutManager,
  and list layout is simply grid layout with one column.  Make it so.

- Use the layout manager to compute item offsets for one page's worth of
  items.  Use that to implement page up/page down.

BUG=25195770

Change-Id: I23ddd5fd38532682e7f099b44e188d8c3fec02be

packages/DocumentsUI/src/com/android/documentsui/Events.java
packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentsAdapter.java

index 99b425e..14d4e2d 100644 (file)
@@ -91,6 +91,8 @@ public final class Events {
             case KeyEvent.KEYCODE_DPAD_RIGHT:
             case KeyEvent.KEYCODE_MOVE_HOME:
             case KeyEvent.KEYCODE_MOVE_END:
+            case KeyEvent.KEYCODE_PAGE_UP:
+            case KeyEvent.KEYCODE_PAGE_DOWN:
                 return true;
             default:
                 return false;
index 70bee3c..0c953ba 100644 (file)
@@ -53,9 +53,7 @@ import android.support.annotation.Nullable;
 import android.support.design.widget.Snackbar;
 import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
-import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.LayoutManager;
 import android.support.v7.widget.RecyclerView.OnItemTouchListener;
 import android.support.v7.widget.RecyclerView.RecyclerListener;
 import android.support.v7.widget.RecyclerView.ViewHolder;
@@ -94,11 +92,13 @@ import com.android.documentsui.RootsCache;
 import com.android.documentsui.Shared;
 import com.android.documentsui.Snackbars;
 import com.android.documentsui.State;
+import com.android.documentsui.State.ViewMode;
 import com.android.documentsui.dirlist.MultiSelectManager.Selection;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.RootInfo;
 import com.android.documentsui.services.FileOperationService;
+import com.android.documentsui.services.FileOperationService.OpType;
 import com.android.documentsui.services.FileOperations;
 import com.google.common.collect.Lists;
 
@@ -155,9 +155,7 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
     private LoaderCallbacks<DirectoryResult> mCallbacks;
     private FragmentTuner mTuner;
     private DocumentClipper mClipper;
-    // These are lazily initialized.
-    private LinearLayoutManager mListLayout;
-    private GridLayoutManager mGridLayout;
+    private GridLayoutManager mLayout;
     private int mColumnCount = 1;  // This will get updated when layout changes.
 
     private MessageBar mMessageBar;
@@ -182,22 +180,6 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
                     }
                 });
 
-        // TODO: Rather than update columns on layout changes, push this
-        // code (or something like it) into GridLayoutManager.
-        mRecView.addOnLayoutChangeListener(
-                new View.OnLayoutChangeListener() {
-
-                    @Override
-                    public void onLayoutChange(
-                            View v, int left, int top, int right, int bottom, int oldLeft,
-                            int oldTop, int oldRight, int oldBottom) {
-                        mColumnCount = calculateColumnCount();
-                        if (mGridLayout != null) {
-                            mGridLayout.setSpanCount(mColumnCount);
-                        }
-                    }
-                });
-
         mRecView.setItemAnimator(new DirectoryItemAnimator(getActivity()));
 
         // TODO: Add a divider between views (which might use RecyclerView.ItemDecoration).
@@ -240,6 +222,13 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
 
         mRecView.setAdapter(mAdapter);
 
+        mLayout = new GridLayoutManager(getContext(), mColumnCount);
+        SpanSizeLookup lookup = mAdapter.createSpanSizeLookup();
+        if (lookup != null) {
+            mLayout.setSpanSizeLookup(lookup);
+        }
+        mRecView.setLayoutManager(mLayout);
+
         mGestureDetector = new ListeningGestureDetector(this.getContext(), new GestureListener());
 
         mRecView.addOnItemTouchListener(mGestureDetector);
@@ -432,41 +421,28 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
     }
 
     /**
-     * Returns a {@code LayoutManager} for {@code mode}, lazily initializing
-     * classes as needed.
+     * Updates the layout after the view mode switches.
+     * @param mode The new view mode.
      */
-    private void updateLayout(int mode) {
-        final LayoutManager layout;
-        switch (mode) {
-            case MODE_GRID:
-                if (mGridLayout == null) {
-                    mGridLayout = new GridLayoutManager(getContext(), mColumnCount);
-                    SpanSizeLookup lookup = mAdapter.createSpanSizeLookup();
-                    if (lookup != null) {
-                        mGridLayout.setSpanSizeLookup(lookup);
-                    }
-                }
-                layout = mGridLayout;
-                break;
-            case MODE_LIST:
-                if (mListLayout == null) {
-                    mListLayout = new LinearLayoutManager(getContext());
-                }
-                layout = mListLayout;
-                break;
-            default:
-                throw new IllegalArgumentException("Unsupported layout mode: " + mode);
+    private void updateLayout(@ViewMode int mode) {
+        mColumnCount = calculateColumnCount(mode);
+        if (mLayout != null) {
+            mLayout.setSpanCount(mColumnCount);
         }
 
         int pad = getDirectoryPadding(mode);
         mRecView.setPadding(pad, pad, pad, pad);
-        // setting layout manager automatically invalidates existing ViewHolders.
-        mRecView.setLayoutManager(layout);
+        mRecView.requestLayout();
         mSelectionManager.handleLayoutChanged();  // RecyclerView doesn't do this for us
         mIconHelper.setViewMode(mode);
     }
 
-    private int calculateColumnCount() {
+    private int calculateColumnCount(@ViewMode int mode) {
+        if (mode == MODE_LIST) {
+            // List mode is a "grid" with 1 column.
+            return 1;
+        }
+
         int cellWidth = getResources().getDimensionPixelSize(R.dimen.grid_width);
         int cellMargin = 2 * getResources().getDimensionPixelSize(R.dimen.grid_item_margin);
         int viewPadding = mRecView.getPaddingLeft() + mRecView.getPaddingRight();
@@ -478,14 +454,12 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
         return columnCount;
     }
 
-    private int getDirectoryPadding(int mode) {
+    private int getDirectoryPadding(@ViewMode int mode) {
         switch (mode) {
             case MODE_GRID:
-                return getResources().getDimensionPixelSize(
-                        R.dimen.grid_container_padding);
+                return getResources().getDimensionPixelSize(R.dimen.grid_container_padding);
             case MODE_LIST:
-                return getResources().getDimensionPixelSize(
-                        R.dimen.list_container_padding);
+                return getResources().getDimensionPixelSize(R.dimen.list_container_padding);
             default:
                 throw new IllegalArgumentException("Unsupported layout mode: " + mode);
         }
@@ -784,7 +758,7 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
                 .show();
     }
 
-    private void transferDocuments(final Selection selected, final int mode) {
+    private void transferDocuments(final Selection selected, final @OpType int mode) {
         // Pop up a dialog to pick a destination.  This is inadequate but works for now.
         // TODO: Implement a picker that is to spec.
         final Intent intent = new Intent(
@@ -1289,12 +1263,14 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
          * @return The adapter position of the destination item. Could be RecyclerView.NO_POSITION.
          */
         private int findTargetPosition(View view, int keyCode) {
-            if (keyCode == KeyEvent.KEYCODE_MOVE_HOME) {
-                return 0;
-            }
-
-            if (keyCode == KeyEvent.KEYCODE_MOVE_END) {
-                return mAdapter.getItemCount() - 1;
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_MOVE_HOME:
+                    return 0;
+                case KeyEvent.KEYCODE_MOVE_END:
+                    return mAdapter.getItemCount() - 1;
+                case KeyEvent.KEYCODE_PAGE_UP:
+                case KeyEvent.KEYCODE_PAGE_DOWN:
+                    return findTargetPositionByPage(view, keyCode);
             }
 
             // Find a navigation target based on the arrow key that the user pressed.
@@ -1330,6 +1306,50 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
         }
 
         /**
+         * Given a PgUp/PgDn event and the current view, find the position of the target view.
+         * This returns:
+         * <li>The position of the topmost (or bottom-most) visible item, if the current item is not
+         *     the top- or bottom-most visible item.
+         * <li>The position of an item that is one page's worth of items up (or down) if the current
+         *      item is the top- or bottom-most visible item.
+         * <li>The first (or last) item, if paging up (or down) would go past those limits.
+         * @param view The view that received the key event.
+         * @param keyCode Must be KEYCODE_PAGE_UP or KEYCODE_PAGE_DOWN.
+         * @return The adapter position of the target item.
+         */
+        private int findTargetPositionByPage(View view, int keyCode) {
+            int first = mLayout.findFirstVisibleItemPosition();
+            int last = mLayout.findLastVisibleItemPosition();
+            int current = mRecView.getChildAdapterPosition(view);
+            int pageSize = last - first + 1;
+
+            if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
+                if (current > first) {
+                    // If the current item isn't the first item, target the first item.
+                    return first;
+                } else {
+                    // If the current item is the first item, target the item one page up.
+                    int target = current - pageSize;
+                    return target < 0 ? 0 : target;
+                }
+            }
+
+            if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
+                if (current < last) {
+                    // If the current item isn't the last item, target the last item.
+                    return last;
+                } else {
+                    // If the current item is the last item, target the item one page down.
+                    int target = current + pageSize;
+                    int max = mAdapter.getItemCount() - 1;
+                    return target < max ? target : max;
+                }
+            }
+
+            throw new IllegalArgumentException("Unsupported keyCode: " + keyCode);
+        }
+
+        /**
          * Requests focus for the item in the given adapter position, scrolling the RecyclerView if
          * necessary.
          *
index 43c2f63..0930c22 100644 (file)
@@ -27,7 +27,6 @@ import android.util.SparseArray;
 
 import com.android.documentsui.State;
 
-import java.nio.channels.UnsupportedAddressTypeException;
 import java.util.List;
 
 /**
@@ -87,7 +86,7 @@ abstract class DocumentsAdapter
      * we adjust sizes.
      */
     GridLayoutManager.SpanSizeLookup createSpanSizeLookup() {
-        throw new UnsupportedAddressTypeException();
+        throw new UnsupportedOperationException();
     }
 
     static boolean isDirectory(Cursor cursor) {