From 20dbdb872fe79d4f235f9a3a2a8f9dd65b047558 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Wed, 21 Sep 2016 15:43:41 +0200 Subject: [PATCH] initial add for recycler view implementation with headers (WIP) --- res/layout/folder_sync_item_header.xml | 57 ++++++ res/layout/folder_sync_layout.xml | 27 +-- .../SectionedRecyclerViewAdapter.java | 184 ++++++++++++++++++ .../ui/activity/FolderSyncActivity.java | 46 +++++ .../android/ui/adapter/FolderSyncAdapter.java | 114 +++++++++++ 5 files changed, 409 insertions(+), 19 deletions(-) create mode 100644 res/layout/folder_sync_item_header.xml create mode 100644 src/com/afollestad/sectionedrecyclerview/SectionedRecyclerViewAdapter.java create mode 100644 src/com/owncloud/android/ui/adapter/FolderSyncAdapter.java diff --git a/res/layout/folder_sync_item_header.xml b/res/layout/folder_sync_item_header.xml new file mode 100644 index 0000000000..152a713f6f --- /dev/null +++ b/res/layout/folder_sync_item_header.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/folder_sync_layout.xml b/res/layout/folder_sync_layout.xml index ff763062dc..28e861579c 100644 --- a/res/layout/folder_sync_layout.xml +++ b/res/layout/folder_sync_layout.xml @@ -34,25 +34,14 @@ - - - - - - - - + diff --git a/src/com/afollestad/sectionedrecyclerview/SectionedRecyclerViewAdapter.java b/src/com/afollestad/sectionedrecyclerview/SectionedRecyclerViewAdapter.java new file mode 100644 index 0000000000..73965abb8e --- /dev/null +++ b/src/com/afollestad/sectionedrecyclerview/SectionedRecyclerViewAdapter.java @@ -0,0 +1,184 @@ +/** + * Copyright 2016 Aidan Follestad + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +package com.afollestad.sectionedrecyclerview; + +import android.support.annotation.IntRange; +import android.support.annotation.Nullable; +import android.support.v4.util.ArrayMap; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.view.ViewGroup; + +import java.util.List; + +/** + * @author Aidan Follestad (afollestad) + */ +public abstract class SectionedRecyclerViewAdapter extends RecyclerView.Adapter { + + protected final static int VIEW_TYPE_HEADER = -2; + protected final static int VIEW_TYPE_ITEM = -1; + + private final ArrayMap mHeaderLocationMap; + private GridLayoutManager mLayoutManager; + private ArrayMap mSpanMap; + private boolean mShowHeadersForEmptySections; + + public SectionedRecyclerViewAdapter() { + mHeaderLocationMap = new ArrayMap<>(); + } + + public abstract int getSectionCount(); + + public abstract int getItemCount(int section); + + public abstract void onBindHeaderViewHolder(VH holder, int section); + + public abstract void onBindViewHolder(VH holder, int section, int relativePosition, int absolutePosition); + + public final boolean isHeader(int position) { + return mHeaderLocationMap.get(position) != null; + } + + /** + * Instructs the list view adapter to whether show headers for empty sections or not. + * + * @param show flag indicating whether headers for empty sections ought to be shown. + */ + public final void shouldShowHeadersForEmptySections(boolean show) { + mShowHeadersForEmptySections = show; + } + + public final void setLayoutManager(@Nullable GridLayoutManager lm) { + mLayoutManager = lm; + if (lm == null) return; + lm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + if (isHeader(position)) + return mLayoutManager.getSpanCount(); + final int[] sectionAndPos = getSectionIndexAndRelativePosition(position); + final int absPos = position - (sectionAndPos[0] + 1); + return getRowSpan(mLayoutManager.getSpanCount(), + sectionAndPos[0], sectionAndPos[1], absPos); + } + }); + } + + @SuppressWarnings("UnusedParameters") + protected int getRowSpan(int fullSpanSize, int section, int relativePosition, int absolutePosition) { + return 1; + } + + // returns section along with offsetted position + private int[] getSectionIndexAndRelativePosition(int itemPosition) { + synchronized (mHeaderLocationMap) { + Integer lastSectionIndex = -1; + for (final Integer sectionIndex : mHeaderLocationMap.keySet()) { + if (itemPosition > sectionIndex) { + lastSectionIndex = sectionIndex; + } else { + break; + } + } + return new int[]{mHeaderLocationMap.get(lastSectionIndex), itemPosition - lastSectionIndex - 1}; + } + } + + @Override + public final int getItemCount() { + int count = 0; + mHeaderLocationMap.clear(); + for (int s = 0; s < getSectionCount(); s++) { + int itemCount = getItemCount(s); + if (mShowHeadersForEmptySections || (itemCount > 0)) { + mHeaderLocationMap.put(count, s); + count += itemCount + 1; + } + } + return count; + } + + /** + * @hide + * @deprecated + */ + @Override + @Deprecated + public final int getItemViewType(int position) { + if (isHeader(position)) { + return getHeaderViewType(mHeaderLocationMap.get(position)); + } else { + final int[] sectionAndPos = getSectionIndexAndRelativePosition(position); + return getItemViewType(sectionAndPos[0], + // offset section view positions + sectionAndPos[1], + position - (sectionAndPos[0] + 1)); + } + } + + @SuppressWarnings("UnusedParameters") + @IntRange(from = 0, to = Integer.MAX_VALUE) + public int getHeaderViewType(int section) { + //noinspection ResourceType + return VIEW_TYPE_HEADER; + } + + @SuppressWarnings("UnusedParameters") + @IntRange(from = 0, to = Integer.MAX_VALUE) + public int getItemViewType(int section, int relativePosition, int absolutePosition) { + //noinspection ResourceType + return VIEW_TYPE_ITEM; + } + + /** + * @hide + * @deprecated + */ + @Override + @Deprecated + public final void onBindViewHolder(VH holder, int position) { + StaggeredGridLayoutManager.LayoutParams layoutParams = null; + if (holder.itemView.getLayoutParams() instanceof GridLayoutManager.LayoutParams) + layoutParams = new StaggeredGridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + else if (holder.itemView.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams) + layoutParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams(); + if (isHeader(position)) { + if (layoutParams != null) layoutParams.setFullSpan(true); + onBindHeaderViewHolder(holder, mHeaderLocationMap.get(position)); + } else { + if (layoutParams != null) layoutParams.setFullSpan(false); + final int[] sectionAndPos = getSectionIndexAndRelativePosition(position); + final int absPos = position - (sectionAndPos[0] + 1); + onBindViewHolder(holder, sectionAndPos[0], + // offset section view positions + sectionAndPos[1], absPos); + } + if (layoutParams != null) + holder.itemView.setLayoutParams(layoutParams); + } + + /** + * @hide + * @deprecated + */ + @Deprecated + @Override + public final void onBindViewHolder(VH holder, int position, List payloads) { + super.onBindViewHolder(holder, position, payloads); + } +} diff --git a/src/com/owncloud/android/ui/activity/FolderSyncActivity.java b/src/com/owncloud/android/ui/activity/FolderSyncActivity.java index 21c305e6ae..3cf1b29ec7 100644 --- a/src/com/owncloud/android/ui/activity/FolderSyncActivity.java +++ b/src/com/owncloud/android/ui/activity/FolderSyncActivity.java @@ -1,17 +1,45 @@ +/** + * Nextcloud Android client application + * + * @author Andy Scherzinger + * Copyright (C) 2016 Andy Scherzinger + * Copyright (C) 2016 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see . + */ + package com.owncloud.android.ui.activity; +import android.app.Activity; import android.content.Intent; import android.os.Bundle; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; import android.view.MenuItem; +import android.view.View; import com.owncloud.android.MainApp; import com.owncloud.android.R; +import com.owncloud.android.ui.adapter.FolderSyncAdapter; /** * Activity displaying all auto-synced folders and/or instant upload media folders. */ public class FolderSyncActivity extends DrawerActivity { private static final String TAG = FolderSyncActivity.class.getSimpleName(); + private RecyclerView mRecyclerView; + private FolderSyncAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { @@ -31,6 +59,24 @@ public class FolderSyncActivity extends DrawerActivity { private void setupContent() { // TODO setup/initialize UI + mRecyclerView = (RecyclerView) findViewById(android.R.id.list); + + final int gridWidth = 4; + mAdapter = new FolderSyncAdapter(this, gridWidth, new FolderSyncAdapter.ClickListener() { + @Override + public void onClick(View view, int section, int relative, int absolute) { + selectItem(FolderSyncActivity.this); + } + }, mRecyclerView); + + final GridLayoutManager lm = new GridLayoutManager(this, gridWidth); + mAdapter.setLayoutManager(lm); + mRecyclerView.setLayoutManager(lm); + mRecyclerView.setAdapter(mAdapter); + } + + public static void selectItem(final Activity context) { + return; } @Override diff --git a/src/com/owncloud/android/ui/adapter/FolderSyncAdapter.java b/src/com/owncloud/android/ui/adapter/FolderSyncAdapter.java new file mode 100644 index 0000000000..cde7473a15 --- /dev/null +++ b/src/com/owncloud/android/ui/adapter/FolderSyncAdapter.java @@ -0,0 +1,114 @@ +/** + * Nextcloud Android client application + * + * @author Andy Scherzinger + * Copyright (C) 2016 Andy Scherzinger + * Copyright (C) 2016 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see . + */ + +package com.owncloud.android.ui.adapter; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter; +import com.owncloud.android.R; + +import java.util.ArrayList; + +/** + * Adapter to display all auto-synced folders and/or instant upload media folders. + */ +public class FolderSyncAdapter extends SectionedRecyclerViewAdapter + implements View.OnClickListener, View.OnTouchListener { + + private static final String TAG = FolderSyncAdapter.class.getSimpleName(); + + private final Context mContext; + private final int mGridWidth; + private final ClickListener mListener; + private final ArrayList mCategories; + private final RecyclerView mRecyclerView; + + public FolderSyncAdapter(Context context, int gridWidth, ClickListener listener, RecyclerView recyclerView) { + mContext = context; + mGridWidth = gridWidth * 2; + mListener = listener; + mCategories = new ArrayList<>(); + mRecyclerView = recyclerView; + } + + @Override + public void onClick(View v) { + + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + return false; + } + + @Override + public int getSectionCount() { + return 0; + } + + @Override + public int getItemCount(int section) { + return 0; + } + + @Override + public void onBindHeaderViewHolder(MainViewHolder holder, int section) { + + } + + @Override + public void onBindViewHolder(MainViewHolder holder, int section, int relativePosition, int absolutePosition) { + + } + + @Override + public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return null; + } + + public interface ClickListener { + void onClick(View view, int section, int relative, int absolute); + } + + public static class MainViewHolder extends RecyclerView.ViewHolder { + + public MainViewHolder(View itemView) { + super(itemView); + image = (ImageView) itemView.findViewById(R.id.image); + title = (TextView) itemView.findViewById(R.id.title); + menuButton = (ImageButton) itemView.findViewById(R.id.syncStatusButton); + mSyncStatusButton = (ImageButton) itemView.findViewById(R.id.settingsButton); + } + + final ImageView image; + final TextView title; + final ImageButton menuButton; + final ImageButton mSyncStatusButton; + } +}