diff --git a/README.md b/README.md index dffd7e3b8..81620740a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ #Include in your project ##Using Maven ```javascript -compile('com.mikepenz:fastadapter:0.0.5-SNAPSHOT@aar') { +compile('com.mikepenz:fastadapter:0.0.6-SNAPSHOT@aar') { transitive = true } diff --git a/app/build.gradle b/app/build.gradle index 2df022231..e59cc81ff 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "com.mikepenz.crossfader.app" minSdkVersion 11 targetSdkVersion 23 - versionCode 5 - versionName '0.0.5-SNAPSHOT' + versionCode 6 + versionName '0.0.6-SNAPSHOT' applicationVariants.all { variant -> variant.outputs.each { output -> @@ -52,14 +52,17 @@ dependencies { compile project(':library') //used to generate the drawer on the left + //https://github.com/mikepenz/MaterialDrawer compile('com.mikepenz:materialdrawer:4.6.3@aar') { transitive = true } //used to generate the Open Source section + //https://github.com/mikepenz/AboutLibraries compile('com.mikepenz:aboutlibraries:5.3.4@aar') { transitive = true } //used to display the icons in the drawer + //https://github.com/mikepenz/Android-Iconics compile 'com.mikepenz:material-design-iconic-typeface:2.2.0.1@aar' //Used for the StickyHeaderSample diff --git a/app/src/main/java/com/mikepenz/fastadapter/app/AdvancedSampleActivity.java b/app/src/main/java/com/mikepenz/fastadapter/app/AdvancedSampleActivity.java index 95c9979cf..5b0847a86 100755 --- a/app/src/main/java/com/mikepenz/fastadapter/app/AdvancedSampleActivity.java +++ b/app/src/main/java/com/mikepenz/fastadapter/app/AdvancedSampleActivity.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.Set; /** * This sample showcases compatibility the awesome Sticky-Headers library by timehop @@ -36,6 +37,10 @@ public class AdvancedSampleActivity extends AppCompatActivity { //save our FastAdapter private FastAdapter fastAdapter; + private ItemAdapter itemAdapter; + + //the ActionMode + private ActionMode actionMode; @Override protected void onCreate(Bundle savedInstanceState) { @@ -55,26 +60,14 @@ protected void onCreate(Bundle savedInstanceState) { //create our adapters final StickyHeaderAdapter stickyHeaderAdapter = new StickyHeaderAdapter(); + itemAdapter = new ItemAdapter(); final HeaderAdapter headerAdapter = new HeaderAdapter(); - final ItemAdapter itemAdapter = new ItemAdapter(); //configure our fastAdapter //as we provide id's for the items we want the hasStableIds enabled to speed up things fastAdapter.setHasStableIds(true); fastAdapter.withMultiSelect(true); fastAdapter.withMultiSelectOnLongClick(true); - fastAdapter.withOnLongClickListener(new FastAdapter.OnLongClickListener() { - @Override - public boolean onLongClick(View v, int position, int relativePosition, IItem item) { - //may check if actionMode is already displayed - startSupportActionMode(new ActionBarCallBack()); - findViewById(R.id.action_mode_bar).setBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(AdvancedSampleActivity.this, R.attr.colorPrimary, R.color.material_drawer_primary)); - - //itemAdapter.removeItemRange(relativePosition, 5); - //itemAdapter.add(position, new PrimaryItem().withName("Awesome :D").withLevel(2).withIdentifier(fastAdapter.getItemCount() + 1000)); - return false; - } - }); fastAdapter.withOnClickListener(new FastAdapter.OnClickListener() { @Override public boolean onClick(View v, int position, int relativePosition, IItem item) { @@ -84,7 +77,27 @@ public boolean onClick(View v, int position, int relativePosition, IItem item) { return true; } } - //Toast.makeText(v.getContext(), ((SectionItem) item).getName().getText(v.getContext()), Toast.LENGTH_SHORT).show(); + //if we are current in CAB mode, and we remove the last selection, we want to finish the actionMode + if (actionMode != null && fastAdapter.getSelections().size() == 1 && item.isSelected()) { + actionMode.finish(); + } + return false; + } + }); + fastAdapter.withOnLongClickListener(new FastAdapter.OnLongClickListener() { + @Override + public boolean onLongClick(View v, int position, int relativePosition, IItem item) { + if (actionMode == null) { + //may check if actionMode is already displayed + actionMode = startSupportActionMode(new ActionBarCallBack()); + findViewById(R.id.action_mode_bar).setBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(AdvancedSampleActivity.this, R.attr.colorPrimary, R.color.material_drawer_primary)); + + //we have to select this on our own as we will consume the event + fastAdapter.select(position); + //we consume this event so the normal onClick isn't called anymore + return true; + } + return false; } }); @@ -162,9 +175,14 @@ class ActionBarCallBack implements ActionMode.Callback { @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - //at the moment just one item which removes selection - fastAdapter.deselect(); - //after selection is removed we probably want finish the actionMode + //we have to refetch the selections array again and again as the position will change after one item is deleted + Set selections = fastAdapter.getSelections(); + while (selections.size() > 0) { + itemAdapter.remove(fastAdapter.getRelativePosition(selections.iterator().next()).relativePosition); + selections = fastAdapter.getSelections(); + } + + //finish the actionMode mode.finish(); return true; } @@ -183,12 +201,17 @@ public boolean onCreateActionMode(ActionMode mode, Menu menu) { @Override public void onDestroyActionMode(ActionMode mode) { + actionMode = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getWindow().setStatusBarColor(Color.TRANSPARENT); } //after we are done with the actionMode we fallback to longClick for multiselect fastAdapter.withMultiSelectOnLongClick(true); + + //actionMode end. deselect everything + fastAdapter.deselect(); } @Override diff --git a/app/src/main/java/com/mikepenz/fastadapter/app/MultiselectSampleActivity.java b/app/src/main/java/com/mikepenz/fastadapter/app/MultiselectSampleActivity.java index e271bd30c..ad9651048 100755 --- a/app/src/main/java/com/mikepenz/fastadapter/app/MultiselectSampleActivity.java +++ b/app/src/main/java/com/mikepenz/fastadapter/app/MultiselectSampleActivity.java @@ -24,10 +24,15 @@ import java.util.ArrayList; import java.util.List; +import java.util.Set; public class MultiselectSampleActivity extends AppCompatActivity { //save our FastAdapter private FastAdapter fastAdapter; + private ItemAdapter itemAdapter; + + //the ActionMode + private ActionMode actionMode; @Override protected void onCreate(Bundle savedInstanceState) { @@ -46,23 +51,38 @@ protected void onCreate(Bundle savedInstanceState) { fastAdapter = new FastAdapter(); //create our adapters + itemAdapter = new ItemAdapter(); final HeaderAdapter headerAdapter = new HeaderAdapter(); - final ItemAdapter itemAdapter = new ItemAdapter(); //configure our fastAdapter //as we provide id's for the items we want the hasStableIds enabled to speed up things fastAdapter.setHasStableIds(true); fastAdapter.withMultiSelect(true); fastAdapter.withMultiSelectOnLongClick(true); + fastAdapter.withOnClickListener(new FastAdapter.OnClickListener() { + @Override + public boolean onClick(View v, int position, int relativePosition, IItem item) { + //if we are current in CAB mode, and we remove the last selection, we want to finish the actionMode + if (actionMode != null && fastAdapter.getSelections().size() == 1 && item.isSelected()) { + actionMode.finish(); + } + return false; + } + }); fastAdapter.withOnLongClickListener(new FastAdapter.OnLongClickListener() { @Override public boolean onLongClick(View v, int position, int relativePosition, IItem item) { - //may check if actionMode is already displayed - startSupportActionMode(new ActionBarCallBack()); - findViewById(R.id.action_mode_bar).setBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(MultiselectSampleActivity.this, R.attr.colorPrimary, R.color.material_drawer_primary)); + if (actionMode == null) { + //may check if actionMode is already displayed + actionMode = startSupportActionMode(new ActionBarCallBack()); + findViewById(R.id.action_mode_bar).setBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(MultiselectSampleActivity.this, R.attr.colorPrimary, R.color.material_drawer_primary)); + + //we have to select this on our own as we will consume the event + fastAdapter.select(position); + //we consume this event so the normal onClick isn't called anymore + return true; + } - //itemAdapter.removeItemRange(relativePosition, 5); - //itemAdapter.add(position, new PrimaryItem().withName("Awesome :D").withLevel(2).withIdentifier(fastAdapter.getItemCount() + 1000)); return false; } }); @@ -119,9 +139,14 @@ class ActionBarCallBack implements ActionMode.Callback { @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - //at the moment just one item which removes selection - fastAdapter.deselect(); - //after selection is removed we probably want finish the actionMode + //we have to refetch the selections array again and again as the position will change after one item is deleted + Set selections = fastAdapter.getSelections(); + while (selections.size() > 0) { + itemAdapter.remove(fastAdapter.getRelativePosition(selections.iterator().next()).relativePosition); + selections = fastAdapter.getSelections(); + } + + //finish the actionMode mode.finish(); return true; } @@ -140,12 +165,17 @@ public boolean onCreateActionMode(ActionMode mode, Menu menu) { @Override public void onDestroyActionMode(ActionMode mode) { + actionMode = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getWindow().setStatusBarColor(Color.TRANSPARENT); } //after we are done with the actionMode we fallback to longClick for multiselect fastAdapter.withMultiSelectOnLongClick(true); + + //actionMode end. deselect everything + fastAdapter.deselect(); } @Override diff --git a/gradle.properties b/gradle.properties index 837cc7263..a9d3cd007 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,8 +19,8 @@ org.gradle.daemon=true org.gradle.parallel=true # Maven stuff -VERSION_NAME=0.0.5-SNAPSHOT -VERSION_CODE=5 +VERSION_NAME=0.0.6-SNAPSHOT +VERSION_CODE=6 GROUP=com.mikepenz POM_DESCRIPTION=FastAdapter Library diff --git a/library/build.gradle b/library/build.gradle index 877507835..7875305d4 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -7,8 +7,8 @@ android { defaultConfig { minSdkVersion 10 targetSdkVersion 23 - versionCode 5 - versionName '0.0.5-SNAPSHOT' + versionCode 6 + versionName '0.0.6-SNAPSHOT' } buildTypes { release { diff --git a/library/src/main/java/com/mikepenz/fastadapter/FastAdapter.java b/library/src/main/java/com/mikepenz/fastadapter/FastAdapter.java index c35f944d2..5b989dc65 100644 --- a/library/src/main/java/com/mikepenz/fastadapter/FastAdapter.java +++ b/library/src/main/java/com/mikepenz/fastadapter/FastAdapter.java @@ -11,6 +11,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; @@ -202,6 +203,7 @@ public boolean onLongClick(View v) { if (!consumed && (mMultiSelect && mMultiSelectOnLongClick)) { handleSelection(pos); } + return consumed; } return false; } @@ -284,9 +286,8 @@ private ItemHolder getInternalItem(int position) { return new ItemHolder(); } - //try to find the adapter for this position - AdapterHolder adapterHolder = getInternalAdapterForPosition(position); + AdapterHolder adapterHolder = getRelativePosition(position); //if found return the itemHolder if (adapterHolder.adapter != null) { @@ -304,7 +305,7 @@ private ItemHolder getInternalItem(int position) { * @param position the global position * @return the adapter which is responsible for this position */ - private AdapterHolder getInternalAdapterForPosition(int position) { + public AdapterHolder getRelativePosition(int position) { if (position < 0) { return new AdapterHolder(); } @@ -417,6 +418,13 @@ public Bundle saveInstanceState(Bundle savedInstanceState) { //------------------------- //------------------------- + /** + * @return a set with the global positions of all selected items + */ + public Set getSelections() { + return mSelections.keySet(); + } + /** * toggles the selection of the item at the given position * @@ -517,6 +525,13 @@ public void deselect() { //------------------------- //------------------------- + /** + * @return a set with the global positions of all opened collapsible items + */ + public Set getOpenedCollapsibleItems() { + return mCollapsibleOpened.keySet(); + } + /** * toggles the collapse state of the given collapsible item at the given position * @@ -542,7 +557,7 @@ public void collapse(int position) { //if this item is not already callapsed and has sub items we go on if (!collapsible.isCollapsed() && collapsible.getSubItems() != null && collapsible.getSubItems().size() > 0) { - AdapterHolder adapterHolder = getInternalAdapterForPosition(position); + AdapterHolder adapterHolder = getRelativePosition(position); //get all positions which are opened or selected and collapse them starting with the biggest number @@ -577,7 +592,7 @@ public void open(int position) { //if this item is not already callapsed and has sub items we go on if (collapsible.isCollapsed() && collapsible.getSubItems() != null && collapsible.getSubItems().size() > 0) { - AdapterHolder adapterHolder = getInternalAdapterForPosition(position); + AdapterHolder adapterHolder = getRelativePosition(position); //if we find the adapter for this item we add the sub items if (adapterHolder.adapter != null && adapterHolder.adapter instanceof IItemAdapter) { @@ -606,8 +621,8 @@ public void open(int position) { */ public void notifyAdapterItemInserted(int position) { //we have to update all current stored selection and collapsed states in our map - mSelections = adjustPositionAfter(mSelections, position, 1); - mCollapsibleOpened = adjustPositionAfter(mCollapsibleOpened, position, 1); + mSelections = adjustPosition(mSelections, position, Integer.MAX_VALUE, 1); + mCollapsibleOpened = adjustPosition(mCollapsibleOpened, position, Integer.MAX_VALUE, 1); notifyItemInserted(position); } @@ -619,8 +634,8 @@ public void notifyAdapterItemInserted(int position) { */ public void notifyAdapterItemRangeInserted(int position, int itemCount) { //we have to update all current stored selection and collapsed states in our map - mSelections = adjustPositionAfter(mSelections, position, itemCount); - mCollapsibleOpened = adjustPositionAfter(mCollapsibleOpened, position, itemCount); + mSelections = adjustPosition(mSelections, position, Integer.MAX_VALUE, itemCount); + mCollapsibleOpened = adjustPosition(mCollapsibleOpened, position, Integer.MAX_VALUE, itemCount); notifyItemRangeInserted(position, itemCount); } @@ -631,8 +646,8 @@ public void notifyAdapterItemRangeInserted(int position, int itemCount) { */ public void notifyAdapterItemRemoved(int position) { //we have to update all current stored selection and collapsed states in our map - mSelections = adjustPositionAfter(mSelections, position, -1); - mCollapsibleOpened = adjustPositionAfter(mCollapsibleOpened, position, -1); + mSelections = adjustPosition(mSelections, position, Integer.MAX_VALUE, -1); + mCollapsibleOpened = adjustPosition(mCollapsibleOpened, position, Integer.MAX_VALUE, -1); notifyItemRemoved(position); } @@ -644,31 +659,45 @@ public void notifyAdapterItemRemoved(int position) { */ public void notifyAdapterItemRangeRemoved(int position, int itemCount) { //we have to update all current stored selection and collapsed states in our map - mSelections = adjustPositionAfter(mSelections, position, itemCount * (-1)); - mCollapsibleOpened = adjustPositionAfter(mCollapsibleOpened, position, itemCount * (-1)); + mSelections = adjustPosition(mSelections, position, Integer.MAX_VALUE, itemCount * (-1)); + mCollapsibleOpened = adjustPosition(mCollapsibleOpened, position, Integer.MAX_VALUE, itemCount * (-1)); notifyItemRangeRemoved(position, itemCount); } + /** * internal method to handle the selections if items are added / removed * - * @param position the global position - * @param adjustBy + * @param positions the positions map which should be adjusted + * @param startPosition the global index of the first element modified + * @param endPosition the global index up to which the modification changed the indices (should be MAX_INT if we check til the end) + * @param adjustBy the value by which the data was shifted + * @return the adjusted map */ - private SortedMap adjustPositionAfter(Map positions, int position, int adjustBy) { + private SortedMap adjustPosition(Map positions, int startPosition, int endPosition, int adjustBy) { SortedMap newPositions = new TreeMap<>(); for (Map.Entry entry : positions.entrySet()) { - if (entry.getKey() < position) { - newPositions.put(entry.getKey(), entry.getValue()); + int position = entry.getKey(); + + //if our current position is not within the bounds to check for we can add it + if (position < startPosition || position > endPosition) { + newPositions.put(position, entry.getValue()); } else if (adjustBy > 0) { - newPositions.put(entry.getKey() + adjustBy, entry.getValue()); - } else if (adjustBy < 0 && entry.getKey() > (position + (-1) * adjustBy)) { - newPositions.put(entry.getKey() + adjustBy, entry.getValue()); - } else if (adjustBy < 0 && entry.getKey() < (position + (-1) * adjustBy)) { - ;//we do not add them anymore. they were removed + //if we added items and we are within the bounds we can simply add the adjustBy to our entry + newPositions.put(position + adjustBy, entry.getValue()); + } else if (adjustBy < 0) { + //if we removed items and we are within the bounds we have to check if the item was removed + //adjustBy is negative in this case + if (position > startPosition + adjustBy && position <= startPosition) { + ;//we are within the removed items range we don't add this item anymore + } else { + //otherwise we adjust our position + newPositions.put(position + adjustBy, entry.getValue()); + } } } + return newPositions; } @@ -807,7 +836,7 @@ private class ItemHolder { /** * an internal class to return the IAdapter and relativePosition at once. used to save one iteration */ - private class AdapterHolder { + public class AdapterHolder { public IAdapter adapter = null; public int relativePosition = -1; } diff --git a/library/src/main/res/values/library_fastadapter_strings.xml b/library/src/main/res/values/library_fastadapter_strings.xml index 4da3ce358..9e0a7c69b 100755 --- a/library/src/main/res/values/library_fastadapter_strings.xml +++ b/library/src/main/res/values/library_fastadapter_strings.xml @@ -10,7 +10,7 @@ FastAdapter . . . ]]> - 0.0.5-SNAPSHOT + 0.0.6-SNAPSHOT https://github.com/mikepenz/FastAdapter apache_2_0 true