ソースを参照

finish implementation of sticky header implementation. This feature was created according to the this implementation : https://stackoverflow.com/questions/32949971/how-can-i-make-sticky-headers-in-recyclerview-without-external-lib

Signed-off-by: alex <alex.plutta@googlemail.com>
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
alex 5 年 前
コミット
792a6b95a6

+ 2 - 1
src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java

@@ -41,6 +41,7 @@ import com.owncloud.android.ui.activities.data.files.FilesRepository;
 import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.adapter.ActivityListAdapter;
+import com.owncloud.android.ui.adapter.StickyHeaderAdapter;
 import com.owncloud.android.ui.interfaces.ActivityListInterface;
 import com.owncloud.android.ui.preview.PreviewImageActivity;
 import com.owncloud.android.ui.preview.PreviewImageFragment;
@@ -174,7 +175,7 @@ public class ActivitiesActivity extends FileActivity implements ActivityListInte
         LinearLayoutManager layoutManager = new LinearLayoutManager(this);
 
         recyclerView.setLayoutManager(layoutManager);
-        recyclerView.addItemDecoration(new ActivityListItemDecoration());
+        recyclerView.addItemDecoration(new ActivityListItemDecoration( adapter));
         recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
 
             @Override

+ 74 - 9
src/main/java/com/owncloud/android/ui/activities/ActivityListItemDecoration.java

@@ -2,35 +2,54 @@ package com.owncloud.android.ui.activities;
 
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.util.Log;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 
-import com.owncloud.android.ui.adapter.ActivityListAdapter;
+import com.owncloud.android.R;
+
+import com.owncloud.android.ui.adapter.StickyHeaderAdapter;
 
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView;
 
 public class ActivityListItemDecoration extends RecyclerView.ItemDecoration {
     private final String TAG = this.getClass().getSimpleName();
-    private View currentHeader;
+    private final StickyHeaderAdapter adapter;
+
+
+    public ActivityListItemDecoration(StickyHeaderAdapter stickyHeaderAdapter) {
+        this.adapter = stickyHeaderAdapter;
+    }
 
     @Override
     public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
         super.onDrawOver(c, parent, state);
 
-
         View topChild = parent.getChildAt(0);
+        if (topChild == null) {
+            return;
+        }
         int topChildPosition = parent.getChildAdapterPosition(topChild);
-        if (topChildPosition == -1) {
+
+        if (topChildPosition == RecyclerView.NO_POSITION) {
             return;
         }
-        ActivityListAdapter adapter = (ActivityListAdapter) parent.getAdapter();
+        View currentHeader = getHeaderViewForItem(topChildPosition, parent);
+        fixLayoutSize(parent, currentHeader);
+        int contactPoint = currentHeader.getBottom();
+        View childInContact = getChildInContact(parent, contactPoint);
 
-        Object topElement = adapter.getActivityAtByPosition(topChildPosition);
+        if (childInContact == null) {
+            return;
+        }
 
-        if (topElement instanceof ActivityListHeader) {
-            currentHeader = parent.getChildAt(0);
-            currentHeader.setBackgroundColor(Color.WHITE);
+        if (adapter.isHeader(parent.getChildAdapterPosition(childInContact))) {
+            moveHeader(c, currentHeader, childInContact);
+            return;
         }
+
         drawHeader(c, currentHeader);
     }
 
@@ -40,4 +59,50 @@ public class ActivityListItemDecoration extends RecyclerView.ItemDecoration {
         header.draw(c);
         c.restore();
     }
+
+    private void moveHeader(Canvas c, View currentHeader, View nextHeader) {
+        c.save();
+        c.translate(0, nextHeader.getTop() - currentHeader.getHeight());
+        currentHeader.draw(c);
+        c.restore();
+    }
+
+    private View getChildInContact(RecyclerView parent, int contactPoint) {
+        View childInContact = null;
+        for (int i = 0; i < parent.getChildCount(); i++) {
+            View currentChild = parent.getChildAt(i);
+            if (currentChild.getBottom() > contactPoint) {
+                if (currentChild.getTop() <= contactPoint) {
+                    childInContact = currentChild;
+                    break;
+                }
+            }
+        }
+        return childInContact;
+    }
+
+    private View getHeaderViewForItem(int itemPosition, RecyclerView parent) {
+        int headerPosition = adapter.getHeaderPositionForItem(itemPosition);
+        int layoutId = adapter.getHeaderLayout(itemPosition);
+        View header = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
+        header.setBackgroundColor(Color.WHITE);
+        adapter.bindHeaderData(header, headerPosition);
+        return header;
+    }
+
+    private void fixLayoutSize(ViewGroup parent, View view) {
+
+        // Specs for parent (RecyclerView)
+        int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY);
+        int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED);
+
+        // Specs for children (headers)
+        int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, parent.getPaddingLeft() + parent.getPaddingRight(), view.getLayoutParams().width);
+        int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, parent.getPaddingTop() + parent.getPaddingBottom(), view.getLayoutParams().height);
+
+        view.measure(childWidthSpec, childHeightSpec);
+        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
+    }
+
+
 }

+ 37 - 10
src/main/java/com/owncloud/android/ui/adapter/ActivityListAdapter.java

@@ -80,12 +80,13 @@ import java.util.List;
 import java.util.Locale;
 
 import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
 /**
  * Adapter for the activity view
  */
-public class ActivityListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+public class ActivityListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements StickyHeaderAdapter {
 
     static final int HEADER_TYPE = 100;
     static final int ACTIVITY_TYPE = 101;
@@ -142,7 +143,7 @@ public class ActivityListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
                 values.add(activity);
             } else {
                 sTime = time;
-                values.add(new ActivityListHeader(sTime));
+                values.add(sTime);
                 values.add(activity);
             }
         }
@@ -238,14 +239,9 @@ public class ActivityListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
             }
         } else {
             ActivityViewHeaderHolder activityViewHeaderHolder = (ActivityViewHeaderHolder) holder;
-            ActivityListHeader header = (ActivityListHeader) values.get(position);
-            activityViewHeaderHolder.title.setText((String) header.getHeadline());
-        }
-    }
-
-    public Object getActivityAtByPosition(int pos) {
-        return values.get(pos);
 
+            activityViewHeaderHolder.title.setText((String) values.get(position));
+        }
     }
 
     private ImageView createThumbnailNew(PreviewObject previewObject) {
@@ -435,6 +431,37 @@ public class ActivityListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
         }
     }
 
+
+    @Override
+    public int getHeaderPositionForItem(int itemPosition) {
+        int headerPosition = 0;
+        do {
+            if (this.isHeader(itemPosition)) {
+                headerPosition = itemPosition;
+                break;
+            }
+            itemPosition -= 1;
+        } while (itemPosition >= 0);
+        return headerPosition;
+    }
+
+    @Override
+    public int getHeaderLayout(int headerPosition) {
+        return R.layout.activity_list_item_header;
+    }
+
+    @Override
+    public void bindHeaderData(View header, int headerPosition) {
+        TextView textView = header.findViewById(R.id.title_header);
+        String headline = (String) values.get(headerPosition);
+        textView.setText(headline);
+    }
+
+    @Override
+    public boolean isHeader(int itemPosition) {
+        return this.getItemViewType(itemPosition) == HEADER_TYPE;
+    }
+
     protected class ActivityViewHolder extends RecyclerView.ViewHolder {
 
         private final ImageView activityIcon;
@@ -453,7 +480,7 @@ public class ActivityListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
         }
     }
 
-    protected class ActivityViewHeaderHolder extends RecyclerView.ViewHolder {
+    public class ActivityViewHeaderHolder extends RecyclerView.ViewHolder {
 
         private final TextView title;
 

+ 34 - 0
src/main/java/com/owncloud/android/ui/adapter/StickyHeaderAdapter.java

@@ -0,0 +1,34 @@
+package com.owncloud.android.ui.adapter;
+
+import android.view.View;
+
+public interface StickyHeaderAdapter {
+    /**
+     * This method gets called by {@link HeaderItemDecoration} to fetch the position of the header item in the adapter
+     * that is used for (represents) item at specified position.
+     * @param itemPosition int. Adapter's position of the item for which to do the search of the position of the header item.
+     * @return int. Position of the header item in the adapter.
+     */
+    int getHeaderPositionForItem(int itemPosition);
+
+    /**
+     * This method gets called by {@link HeaderItemDecoration} to get layout resource id for the header item at specified adapter's position.
+     * @param headerPosition int. Position of the header item in the adapter.
+     * @return int. Layout resource id.
+     */
+    int getHeaderLayout(int headerPosition);
+
+    /**
+     * This method gets called by {@link HeaderItemDecoration} to setup the header View.
+     * @param header View. Header to set the data on.
+     * @param headerPosition int. Position of the header item in the adapter.
+     */
+    void bindHeaderData(View header, int headerPosition);
+
+    /**
+     * This method gets called by {@link HeaderItemDecoration} to verify whether the item represents a header.
+     * @param itemPosition int.
+     * @return true, if item at the specified adapter's position represents a header.
+     */
+    boolean isHeader(int itemPosition);
+}