GithubHelp home page GithubHelp logo

davideas / flexibleadapter Goto Github PK

View Code? Open in Web Editor NEW
3.6K 115.0 540.0 10.6 MB

Fast and versatile Adapter for RecyclerView which regroups several features into one library to considerably improve the user experience :-)

License: Apache License 2.0

Java 100.00%
android recyclerview adapter multi-select actionmode undo viewholder expandable collapsable draggable swipeable sections sticky-headers headers footers endless-scroll selection-coherence fastscroll search-filters livedata

flexibleadapter's Introduction

Android Arsenal Download API Licence Methods and Size

FlexibleAdapter

  • v5.1.0 built on 2018.09.30 with AndroidX
  • If you come from previous versions, update your code following the Wiki page Migrations.
  • Please read also issues and releases.

One Adapter many Apps

📣 When initially Android®️ team introduced the RecyclerView widget, we had to implement a custom Adapter in several applications, again and again to provide the items for our views.
We didn't know how to add selection and to combine all the use cases in the same Adapter. Since I created this library, it has become easy to configure multiple views and now, nobody wants to use a ListView anymore.

The idea behind is to regroup multiple features in a unique library, without the need to customize and import several third libraries not compatible among them.

The FlexibleAdapter helps developers to simplify this process without worrying too much about the Adapter anymore. It's easy to use, it has predefined logic for different situations and prevents common mistakes.
This library is configurable and it guides the developers to create a better user experience and now, even more with the new features.

Features in main library

  • Simple, Single & Multi selection mode.
  • Mapping multi-view types with Item interfaces.
  • Predefined ViewHolders with (child) click listeners and others callbacks.
  • Async Updates with optional DiffUtil for small lists.
  • Async Filter with spannable text (:eyeglasses:); Result list is animated; With optional original list; Works with sub items; Multi filter.
  • High performance filter and updates for medium and big lists.
  • Headers and Sections with sticky behaviour fully clickable and collapsible, elevation, transparency and automatic linkage on item move!
  • Scrollable Headers and Footers items that lay respectively at the top and at the bottom of the main items.
  • Expandable items with Selection Coherence and multi-level expansion.
  • Drag&Drop and Swipe-To-Dismiss with Leave-Behind pattern and with Selection Coherence.
  • Innovative bottom and top EndlessScroll (No OnScrollListener).
  • Customizable FastScroller with several features.
  • Customizable Scrolling Animations based on adapter position and beyond.
  • Customizable Animations when adding and removing items.
  • LayoutUtils for orientation, span count and visible items calculation.
  • Support for any thirds LayoutManagers.
  • Easy runtime position calculation for adding/moving items in sections.
  • Custom Tags for multiple adapter instances that ease our debug.
  • Comprehensive Wiki pages and JavaDoc documentation.

UI extension library 👓

  • Faster setup selection with ActionModeHelper.
  • Faster setup for item restoration with UndoHelper; Works with expandable and swiped items too!
  • Basic empty view handling with EmptyViewHelper.
  • Advanced item decoration (customizable dividers, sections gap and item offsets).
  • 3 layout managers that support smooth scrolling.
  • FlexibleUtils for text highlighting, accent color fetching, API versions.
  • DrawableUtils for dynamic backgrounds with ripple effect (No XML).

Others experimental extension libraries

Setup

build.gradle

repositories {
    jcenter()
}
dependencies {
    // Using JCenter
    implementation 'eu.davidea:flexible-adapter:5.1.0'
    implementation 'eu.davidea:flexible-adapter-ui:1.0.0'
    implementation 'eu.davidea:flexible-adapter-livedata:1.0.0-b3'
    // From 1.0.0-b3, library is compiled with data binding v2
    implementation 'eu.davidea:flexible-adapter-databinding:1.0.0'
}

Stay Updated

Flexible Adapter UI Live Data Data Binding
5.1.0
1.0.0
1.0.0-b3
1.0.0

Wiki!

I strongly recommend to read the Wiki pages, where you can find a comprehensive Tutorial.

Pull requests / Issues / Improvement requests

Feel free to contribute and ask!
Active discussions:

Under the hood

Some simple features have been implemented, thanks to some Blogs (see at the bottom of the page), merged and methods have been improved for speed and scalability.

  • At lower level there is SelectableAdapter class. It provides selection features and it's able to maintain the state after the rotation: you just need to call the onSave/onRestore methods from the Activity!
  • At middle level, the AnimatorAdapter class has been added to give some animation at startup and when user scrolls.
  • At front level, the core class FlexibleAdapter. It holds and handles the main list, performs actions on all different types of item paying attention at the adding and removal of the items, as well as the new concept of "selection coherence".
  • Useful extensions and helpers have been added during the time to simplify the development.
  • Item interfaces and predefined ViewHolders complete the whole library giving more actions to the items and configuration options to developers.

Showcase of the demo App

You can download the latest demo App from the latest release page OR run it with the emulator.
This Wiki page will give you a short briefing of the demo App.

Overall Adapter Animations Undo Staggered Layout

Endless Instagram Expandables Multi Selection & SC

Drag & Drop swipe-to-dismiss1 swipe-to-dismiss2 ViewPager

Async Filter Search Filter Search Sections

Change Log

Latest release

v5.1.0 - 2018.09.30

Old releases

v5.0.6 - 2018.09.16 | v5.0.5 - 2018.06.03 | v5.0.4 - 2018.05.01 | v5.0.3 - 2018.03.23 | v5.0.2 - 2018.03.17 | v5.0.1 - 2018.03.11 | v5.0.0 - 2018.03.04

Limitations

Item half swipe cannot be implemented due to how the android.support.v7.widget.helper.ItemTouchHelper is done, also clicks on rear view are not possible, same reason. Half swipe can be done with others means, please see issues #98 and #100. See also commits of Apr 25, 2016.

Thanks

Inspired by
Special thanks goes to
  • Martin Guillon (Akylas) to have contributed at the development of the new technique for the Sticky Header.
  • Arpinca who added new features for FastScroller like autoHide and ignoreTouchesOutsideHandle and more.
Donations

This library is currently free to use and modify, if you would like to say Thank You by donating any amount, you're very welcome! Here the link to PayPal.me:

PayPal.me

Imported libraries

  • The library LollipopContactsRecyclerViewFastScroller has been imported, heavily improved and adapted to work in conjunction with AnimatorAdapter.
  • The library sticky-headers-recyclerview was initially imported, then it was removed in favor of the new technique able to manage a real View and so to handle the click events properly.
  • The item spacing technique comes from the library CommonItemDecoration and it has been improved with new features.

Apps that use this Adapter

It will be a pleasure to add your App here, once it is published.

Module.org | Neuronizer | Nextcloud Talk | Socio - Shake and Connect! | Shibagram | BNVR Client

License

FlexibleAdapter & Extensions

Copyright 2015-2018 Davide Steduto, Davidea Solutions Sprl

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.

FastScroller

Copyright 2016-2017 AndroidDeveloperLB, Davide Steduto & Arpinca

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.

flexibleadapter's People

Contributors

arpinca avatar borisdamato avatar danielpassos avatar davideas avatar gwailotr0n5000 avatar jblejder avatar jeanpimentel avatar jlwatkins avatar johnjohndoe avatar jrcacd avatar lauren2020 avatar long1eu avatar magneticflux- avatar masc3d avatar ouyangzn avatar shymmq avatar stepansanda avatar zoopolitic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flexibleadapter's Issues

How to enable sticky headers?

My header model

` */
public class MyNewsBigItem extends AbstractFlexibleItem<MyNewsBigItem.ViewHolder> implements ISectionable<MyNewsBigItem.ViewHolder, MyNewsHeaderItem> {

private MyNewsHeaderItem mHeader;

@Override
public MyNewsHeaderItem getHeader() {
    return mHeader;
}

@Override
public MyNewsHeaderItem setHeader(MyNewsHeaderItem header) {
    mHeader = header;
    return header;
}

`
And my adapter looks like this:

`public class MyNewsFlexibleAdapter extends FlexibleAdapter {

public MyNewsFlexibleAdapter(List<AbstractFlexibleItem> items) {
    super(items);
    enableStickyHeaders(3);
    MyNewsHeaderItem h1 = createHeaderItem();
    MyNewsHeaderItem h2 = createHeaderItem();
    MyNewsHeaderItem h3 = createHeaderItem();
    MyNewsHeaderItem h4 = createHeaderItem();

    addItem(getItemCount(), h1);
    addItem(getItemCount(), createbigItem(h1));
    addItem(getItemCount(), createbigItem(h1));
    addItem(getItemCount(), createbigItem(h1));
    addItem(getItemCount(), createbigItem(h1));
    addItem(getItemCount(), h2);
    addItem(getItemCount(), createbigItem(h2));
    addItem(getItemCount(), createbigItem(h2));
    addItem(getItemCount(), createbigItem(h2));
    addItem(getItemCount(), createbigItem(h2));
    addItem(getItemCount(), createbigItem(h2));
    addItem(getItemCount(), h3);
    addItem(getItemCount(), createbigItem(h3));
    addItem(getItemCount(), h4);
    addItem(getItemCount(), createbigItem(h4));
    addItem(getItemCount(), createbigItem(h4));
    addItem(getItemCount(), createbigItem(h4));
    addItem(getItemCount(), createbigItem(h4));
}

`

Everything is shown correctly on the app, but the headers are not sticky. Where I'm wrong?
I've also tried to call the enableStickyHeaders after I set the adapter to the recyclerview. no success.

Populate list of items

It is not so clear only looking for the API or reading the wiki, you need populate the list of items in FlexibleAdapter using the method filterItems. If you don't do that you will get a RuntimeException.

IMHO populate the list of items should be a construction responsabilite. Wdyt?.

Sticky headers

While scrolling, an header or a section can be "sticky" to the top (or to the bottom?).
It should pushed away when scrolling in favor of a new headers.

The adapter is highlighting text but not filtering

I have copied the two classes and extended the Flexible adapter class in my adapter. My problem now is when i enter text in search view the text gets highlighted but it does not get filtered. I know im going wrong somewhere but i cant figure out where. Below is the code of my adapter class.

    public class FertilizerListAdapter extends FlexibleAdapter<FertilizerListAdapter.FertilizerViewHolder, Fertilizer> {
    List<Fertilizer> items;
    Context context;
    LayoutInflater mInflater;
    public FertilizerListAdapter(Context context, List<Fertilizer> items, String param) {
        this.items = items;
        this.context = context;
        mInflater = LayoutInflater.from(context);
        updateDataSet(param);
    }
    @Override
    public void updateDataSet(String param) {

    }
    @Override
    public FertilizerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View productItem = LayoutInflater.from(parent.getContext()).inflate(R.layout.fertilizer_list_item, parent, false);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            productItem.setLayerType(View.LAYER_TYPE_HARDWARE, null);
        FertilizerViewHolder productItemVH = new FertilizerViewHolder(productItem);
        return productItemVH;

    }

    @Override
    public void onBindViewHolder(FertilizerViewHolder holder, final int position) {
        Fertilizer item = items.get(position);
        Typeface custom_font = Typeface.createFromAsset(context.getAssets(), "fonts/RobotoSlab-Light.ttf");
        holder.mProductName.setText(items.get(position).sProductName);
        holder.mProductPrice.setText(items.get(position).sProductPrice);
        holder.mShortDesc.setText(items.get(position).sShortDesc);
        ColorGenerator generator = ColorGenerator.DEFAULT;
        int color = generator.getRandomColor();
        TextDrawable drawable = TextDrawable.builder()
                .beginConfig()
                .textColor(Color.WHITE)
                .toUpperCase()
                .useFont(custom_font)
                .fontSize(30)
                .endConfig()
                .buildRound(String.valueOf(items.get(position).sProductName.charAt(0)), color);

        Picasso.with(context)
                .load(items.get(position).sProductImage)
                .placeholder(drawable)
                .error(drawable)
                .into(holder.mProductImage);


        holder.itemView.setTag(item);
        if (hasSearchText()) {
            setHighlightText(holder.mProductName, item.sProductName, mSearchText);
            setHighlightText(holder.mShortDesc, item.sShortDesc, mSearchText);
        } else {
            holder.mProductName.setText(item.sProductName);
            holder.mShortDesc.setText(item.sShortDesc);
        }
        holder.mView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(context, FertilizerView.class);
                i.putExtra("ProductApplication", (items.get(position).sApplication));
                i.putExtra("ProductAvailability", (items.get(position).sAvailability));
                i.putExtra("ProductBenefits", (items.get(position).sBenefits));
                i.putExtra("ProductChemicalContent", (items.get(position).sChemicalContent));
                i.putExtra("ProductCrops", (items.get(position).sCrops));
                i.putExtra("ProductBacgroundImage", (items.get(position).sProductBackground));
                i.putExtra("ProductImage", (items.get(position).sProductImage));
                i.putExtra("ProductName", (items.get(position).sProductName));
                i.putExtra("ProductPrice", (items.get(position).sProductPrice));
                i.putExtra("ProductDescription", (items.get(position).sShortDesc));
                context.startActivity(i);
            }
        });
    }





    @Override
    public int getItemCount() {
        return items.size();
    }


    public static class FertilizerViewHolder extends RecyclerView.ViewHolder {
        ImageView mProductImage;
        TextView mProductName;
        TextView mShortDesc, mProductPrice;
        View mView;

        public FertilizerViewHolder(View itemView) {
            super(itemView);
            mView = itemView;
            mProductImage = (ImageView) itemView.findViewById(R.id.product_image);
            mProductName = (TextView) itemView.findViewById(R.id.product_name);
            mProductPrice = (TextView) itemView.findViewById(R.id.price);
            mShortDesc = (TextView) itemView.findViewById(R.id.shortDesc);

        }
    }
    private void setHighlightText(TextView textView, String text, String searchText) {
        Spannable spanText = Spannable.Factory.getInstance().newSpannable(text);
        int i = text.toLowerCase(Locale.getDefault()).indexOf(searchText);
        if (i != -1) {
            spanText.setSpan(new ForegroundColorSpan(Utils.getColorAccent(context)), i,
                    i + searchText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            spanText.setSpan(new StyleSpan(Typeface.BOLD), i,
                    i + searchText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            textView.setText(spanText, TextView.BufferType.SPANNABLE);
        } else {
            textView.setText(text, TextView.BufferType.NORMAL);
        }
    }
    @Override
    protected boolean filterObject(Fertilizer fertilizer, String constraint) {
        String valueText = fertilizer.sProductName;
        //Filter on Title
        if (valueText != null && valueText.toLowerCase().contains(constraint)) {
            return true;
        }
        //Filter on Subtitle
        valueText = fertilizer.sShortDesc;
        return valueText != null && valueText.toLowerCase().contains(constraint);
    }
    }

Animate items while filtering

Adapter should not call notifyDataSetChanged() anymore while filtering. Instead items should be added and/or removed from the adapter list depending by the searchText and the final change should be animated.

Add the concepts of isEnabled - isSelectable - isSwipeable - isDraggable

TODO
isEnabled = no simple nor single nor multi selection, no touchable.
isSelectable = no single nor multi selection, cannot be deleted by user, probably not touchable, ok to delete programmaticaly.
isSwipeable = just cannot be swiped
isDraggable = just cannot be dragged
isHidden = just cannot be visible

DONE
isExpanded
isHidden (for searchView)

Example with checkbox

Could you provide an example with checkbox selection?
For some users a long click to select is sometimes "hard to understand" or in other words "they don't know what to do". ;-)

*** Snapshots and Pre-Releases for FlexibleAdapter v5.0.0 ***

This is an open discussion to keep track of all the work in progress of the next coming pre-releases in order to arrive to a stable and final version.

In this major release (5.0.0) there will be lot of new features, while trying to simplify the configuration to speed up the development. A full list of the features and improvements, with their status, can be found here Milestone 5.0.0. However we can identify them in:

  • Expandable item with selection coherence.
  • Predefined ViewHolders.
  • Adapter Animations.
  • Drag&Drop and Swipe actions.
  • New concept of Item.
  • Simplify the constructor.
  • New FastScroller to include in the library.
  • Rewrite filtering and restore methods.
  • Headers/Sections
  • etc..

In order to do them, I am completely changing and refactoring the code, names and how the Adapter will be extended to override its methods.

Gradle configuration

allprojects {
    repositories {
        // For [pre-]releases
        jcenter()
        // For SNAPSHOT
        maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
    }
}
// Don't cache SNAPSHOT (changing) dependencies.
configurations.all {
    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}
dependencies {
    // JCenter for PreReleases
    compile 'eu.davidea:flexible-adapter:5.0.0-rc3'
    compile 'eu.davidea:flexible-adapter-livedata:1.0.0-b1'
    compile 'eu.davidea:flexible-adapter-databinding:1.0.0-b2'

    // Maven for SNAPSHOT
    compile ('eu.davidea:flexible-adapter:5.0.0-SNAPSHOT') {
        changing = true;
    }
}

As soon as a new SNAPSHOT is ready and usable I will communicate it here.

Suggestions

  • Feel free to Fork but better to advise me in advance what are you going to modify in order to coordinates the commits and the work.
  • Also, feel free to test and report back here or by opening a new Issue.

Naming convention adopted

Version Reason for publishing Description
5.0.0-SNAPSHOT continuous development A library with many changes that might be unstable, nice to have to early benefit of the latest bug fix, improvements and new features as soon as they are implemented.
It will replace the existent library (with that name) in the maven snapshot repository. So you don't have to manually change the version in the dependencies, but expect to upgrade your code in order to continue to compile your project.

Gradle has internal caching system with default time of 24h. Passed this time it will fetch the new Snapshot file from repository, if a new exists.
5.0.0-bX beta pre-release Usable beta library with some new features to test.
5.0.0-rcX release candidates Nearly completed library with good stability and improved features
5.0.0 release The stable library with all new features confirmed and tested.

ItemAnimators coherent with AnimatorViewHolder

Item Animation should not overlap with Adapter Animation.
Modify ItemAnimators to be intercepted by AnimatorVH, that can return false to stop animator.

Add custom animation on children ItemViews.

Filter using interface rather than just a string

Rather than just passing a String constraint to the filter method, it would be nice if you could pass an interface along the lines of:

boolean isAllowed(T item)

The item would be passed to the isAllowed method and allow you to filter items with different conditions.

I am happy to work on a PR for this. Just wanted to check if this was something you were interested in supporting.

Undo Helper

Should launch the SnackBar automatically and interact with the Adapter.

v5.0.0-b4: Inconsistency detected issue with showAllHeaders() method.

Hi @davideas

Thanks for great lib.

I am using FlexibleAdapter to display sticky headers in RecyclerView. It is working fine for the first time loading list data using adapter, however, I am getting below crash issue while pushing fresh list items in adapter for the second time onwards.

Below is my code.

Code in onCreateView() method:

mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
        mRecyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));

        //mRecyclerView.setHasFixedSize(true); //Size of RV will not change
        mRecyclerView.setItemAnimator(new DefaultItemAnimator() {
            @Override
            public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {
                //NOTE: This allows to receive Payload objects on notifyItemChanged called by the Adapter!!!
                return true;
            }
        });
        mRecyclerView.addItemDecoration(new DividerItemDecoration(getContext(), R.drawable.divider));

code in refreshData() method:(which will be called everytime when user want to refresh data)

ArrayList<AbstractFlexibleItem> items = new ArrayList<AbstractFlexibleItem>();
.....
......
add AbstractFlexibleItem objects(headers and simple items) to items.
....
.......
mAdapter = new FeedAdapter(items, FeedFragment.this);
mAdapter.setRemoveOrphanHeaders(false);
mRecyclerView.setAdapter(mAdapter);
mAdapter.showAllHeaders();
mAdapter.enableStickyHeaders(3);

Note: FeedAdapter extends FlexibleAdapter

I am not observing the crash issue while commenting showAllHeaders() method call, however, no headers are showing due to this.

Can you please help me to get out of this issue?


E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.code, PID: 23833
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 15(offset:21).state:21
  at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4405)
  at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4363)
  at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1961)
  at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1370)
  at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1333)
  at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:562)
  at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2864)
  at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3071)
  at android.view.View.layout(View.java:15140)
  at android.view.ViewGroup.layout(ViewGroup.java:4866)
  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:515)
  at android.widget.FrameLayout.onLayout(FrameLayout.java:450)
  at android.view.View.layout(View.java:15140)
  at android.view.ViewGroup.layout(ViewGroup.java:4866)
  at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1627)
  at android.view.View.layout(View.java:15140)
  at android.view.ViewGroup.layout(ViewGroup.java:4866)
  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1888)
  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1742)
  at android.widget.LinearLayout.onLayout(LinearLayout.java:1651)
  at android.view.View.layout(View.java:15140)
  at android.view.ViewGroup.layout(ViewGroup.java:4866)
  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:515)
  at android.widget.FrameLayout.onLayout(FrameLayout.java:450)
  at android.view.View.layout(View.java:15140)
  at android.view.ViewGroup.layout(ViewGroup.java:4866)
  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1888)
  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1742)
  at android.widget.LinearLayout.onLayout(LinearLayout.java:1651)
  at android.view.View.layout(View.java:15140)
  at android.view.ViewGroup.layout(ViewGroup.java:4866)
  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:515)
  at android.widget.FrameLayout.onLayout(FrameLayout.java:450)
  at android.view.View.layout(View.java:15140)
  at android.view.ViewGroup.layout(ViewGroup.java:4866)
  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1888)
  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1742)
  at android.widget.LinearLayout.onLayout(LinearLayout.java:1651)
  at android.view.View.layout(View.java:15140)
  at android.view.ViewGroup.layout(ViewGroup.java:4866)
  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:515)
  at android.widget.FrameLayout.onLayout(FrameLayout.java:450)
  at android.view.View.layout(View.java:15140)
  at android.view.ViewGroup.layout(ViewGroup.java:4866)
  at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2342)
  at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2048)
  at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1211)
  at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6282)
  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:788)
  at android.view.Choreographer.doCallbacks(Choreographer.java:591)
  at android.view.Choreographer.doFrame(Choreographer.java:560)
  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:774)
  at android.os.Handler.handleCallback(Handler.java:808)
  at android.os.Handler.dispatchMessage(Handler.java:103)
  at android.os.Looper.loop(Looper.java:193)
  at android.app.ActivityThread.main(ActivityThread.java:5341)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:515)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:825)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:641)
E/NativeCrypto: ssl=0x60990e98 cert_verify_callback x509_store_ctx=0x61259940 arg=0x0
E/NativeCrypto: ssl=0x60990e98 cert_verify_callback calling verifyCertificateChain authMethod=ECDHE_RSA

Headers are shown multiple times

How I initialize my adapter:

mAdapter = new MyNewsFlexibleAdapter(new ArrayList<AbstractFlexibleItem>());
        mAdapter.setDisplayHeadersAtStartUp(true);//Show Headers at startUp!`

// And after I get my recycler view
mRecycler.setAdapter(mAdapter);
        mAdapter.enableStickyHeaders(3);

I have this method in my adapter

 public void addNews(List<News> newsList) {
        // create header
        int categoryId = newsList.get(0).categoryId;
        MyNewsHeaderItem header = new MyNewsHeaderItem(categoryId);
        // add items for header
        for (News news : newsList) {
            MyNewsSmallItem newsSmallItem = new MyNewsSmallItem(news, header);
            addItem(getItemCount(), newsSmallItem);
        }
        showAllHeaders();
    }

And this method is called multiple times, if I call this method for 2 times I would like to have something like this

  • header
  • item
  • item
  • header
  • item
  • item

And instead the first header is added twice!

  • header
  • header
  • item
  • item
  • header
  • item
  • item

If this method gets called 3 times, the first header would be added 3 times, the second one twice...

How can I fix this ? If I don't call showAllHeaders() the headers won't show up, and this method can be called any time..

EDIT :
I found a solution

 hideAllHeaders();
        showAllHeaders();

EDIT 2: this will cause a NPE at FlexibleAdapter 827

And another question, how can I completely clear the list? Without creating a new adapter of course..

updateDataSetAsync method doesn't give any added value

updateDataSetAsync() will be deprecated in 4.2.0 and removed from next major release.

The inner class FilterAsyncTask will not be used anymore to load data at startup: there's no advantage to use an Asynchronous loading, usually the list is already loaded Asynchronously somewhere else.

Sticky headers are not pushing one another from screen when using grid layout manager.

Hi,
I am testing 5.0.0-b5 and found a bug in StickyHeaderHelper#translateHeader() on row 151. You are expecting next potential header on position 1. But if for example I use 3 columns in my grid, you will always get current header until all items in section are off the screen.

So you won't get this nice pushing of one header off the screen by another header.

I was able to reproduce this in your demo app, when I changed span count of grid to 3 and span size of all items except header to 1.

Anyway, thanks for great work.

Sticky headers inside section

Is it possible to configure the FlexibleAdapter to have sticky headers inside a section, not above it?
What I mean is to have items look like so:

=== section name ===
== sticky header 1 ==
= item 1.1 =
= item 1.2 =
== sticky header 2 ==
= item 2.1 =
= item 2.2 =

The point is that the section names do not stick to the top of the screen but the sticky headers which are inside the sections do.

Divider at the end of a section

Using RecyclerView.ItemDecoration creates a divider after every row and header. Is there a way to have the divider only show at the end of a section? Or should I make the divider a row?

Section header 1
section item 1
section item 2
divider
Section header 2
section item 1 (no divider here)

Add unit tests

The project need to have some unit tests to help us to refactor/introduce new features.

Sticky Headers clickable

Once a header is drawn over others elements, it should be clickable.
This is not a bug due to the different representation/implementation of the view in the RecyclerView.

New FastScroller

New FastScroller:

  • Autohide.
  • Improving configuration.
  • Bubble can be enabled/disabled and position tuned on Y axis too.
  • FastScrollerDelegate and Interface, to be used standalone outside of FlexibleAdapter (still inside the package).
  • IgnoreTouchesOutsideHandle
  • Extends / Separate the copyright for FastScroller in the readme.
  • Customizable minimum scroll amount to show the FastScroller.

Other sources:

Bug on Sample App

When clicking sub item or remove sub item, will be appear following error:

FATAL EXCEPTION: main Process: eu.davidea.examples.flexibleadapter, PID: 28911 java.lang.ClassCastException: eu.davidea.examples.models.SubItem cannot be cast to eu.davidea.examples.models.SimpleItem at eu.davidea.examples.flexibleadapter.MainActivity.extractTitleFrom(MainActivity.java:739) at eu.davidea.examples.flexibleadapter.MainActivity.onItemSwipe(MainActivity.java:492) at eu.davidea.flexibleadapter.FlexibleAdapter.onItemSwiped(FlexibleAdapter.java:2331) at eu.davidea.flexibleadapter.helpers.ItemTouchHelperCallback.onSwiped(ItemTouchHelperCallback.java:159) at android.support.v7.widget.helper.ItemTouchHelper$4.run(ItemTouchHelper.java:669) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5254) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

Sample Project?

A working example would be greatly appreciated? The ExampleAdapter is not really clear since you renamed ManageLabelsAdapter to it, along with a plethora of "missing" classes associated with it. Thank you for the library.

Sticky Headers not working on 5.0.0-SNAPSHOT

I'm trying to follow what you have in the example app, but I can't get sticky headers to work with my app. The header view shows up but isn't sticky.

gradle

compile 'eu.davidea:flexible-adapter:5.0.0-SNAPSHOT'
maven {url = "https://oss.sonatype.org/content/repositories/snapshots/" } //For Snapshots

Adapter

public class RecentContactsAdapter extends FlexibleAdapter<AbstractFlexibleItem> {

    private final Context context;

    private List<GroupedRecentCall> groupedRecentCallList;
    private Map<String, String> callLogToPhoneMap;
    private List<AbstractFlexibleItem> recentCallsWithHeaders;
    private Map<Integer, LocalDataForRecentCall> localMap;

    public RecentContactsAdapter(Context context, List<AbstractFlexibleItem> recentCallsWithHeaders) {
        super(recentCallsWithHeaders);

        this.recentCallsWithHeaders = recentCallsWithHeaders;
        this.context = context;

        localMap = new HashMap<Integer, LocalDataForRecentCall>();

        callLogToPhoneMap = new HashMap<String, String>();
    }
}

Header

public class RecentContactHeaderItem extends AbstractHeaderItem<RecentContactHeaderItem.ViewHolder> {

    private String title;

    public RecentContactHeaderItem(String title) {
        super();
        this.title = title;}

    @Override
    public boolean equals(Object o) {
        if (o instanceof String) {
            String oo = (String) o;
            return oo.equals(this.title);
        }
        return false;
    }

    @Override
    public int getLayoutRes() {
        return R.layout.recent_contacts_header;
    }

    @Override
    public ViewHolder createViewHolder(FlexibleAdapter adapter, LayoutInflater inflater, ViewGroup parent) {
        return new ViewHolder(inflater.inflate(getLayoutRes(), parent, false), adapter);
    }

    @Override
    public void bindViewHolder(FlexibleAdapter adapter, ViewHolder holder, int position, List payloads) {
        holder.titleView.setText(title);
    }

    public static class ViewHolder extends FlexibleViewHolder {
        public OpenSansTextView titleView;

        public ViewHolder(View view, FlexibleAdapter adapter) {
            super(view, adapter);
            this.titleView = (OpenSansTextView) view.findViewById(R.id.title);
        }
    }
}

Section item

public class RecentContactItem extends AbstractSectionableItem<RecentContactItem.ViewHolder, RecentContactHeaderItem> {

    private final String TAG = "RecentContactItem";

    private Context context;
    private SimpleDateFormat timeFormatter;
    private SimpleDateFormat dateFormatter;
    private GroupedRecentCall groupedRecentCall;

    private Map<String, String> callLogToPhoneMap;
    private Map<Integer, RecentContactsAdapter.LocalDataForRecentCall> localMap;

    private RoundedTransformation roundedTransformer;
    private Bitmap defaultAvatar;

    public RecentContactItem(Context context, RecentContactHeaderItem header, SimpleDateFormat timeFormatter,
                             SimpleDateFormat dateFormatter, GroupedRecentCall groupedRecentCall,
                             HashMap<Integer, RecentContactsAdapter.LocalDataForRecentCall> localMap,
                             HashMap<String, String> callLogToPhoneMap) {
        this.context = context;
        setHeader(header);
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof CallLogObject) {
            GroupedRecentCall inItem = (GroupedRecentCall) o;
            return this.groupedRecentCall.getCallLogObject().getCallDate().equals(inItem.getCallLogObject().getCallDate());
        }
        return false;
    }

    @Override
    public int getLayoutRes() {
        return R.layout.recent_contacts_item;
    }

    @Override
    public ViewHolder createViewHolder(FlexibleAdapter adapter, LayoutInflater inflater, ViewGroup parent) {
        return new ViewHolder(inflater.inflate(getLayoutRes(), parent, false), adapter);
    }

    @Override
    public void bindViewHolder(FlexibleAdapter adapter, ViewHolder holder, int position, List payloads) {
        // binding
    }

public static final class ViewHolder extends FlexibleViewHolder {

        public ImageView avatarView;
        public OpenSansTextView moreCountView;
        public OpenSansTextView text1View;
        public OpenSansTextView text2View;
        public ImageView moreView;

        public ViewHolder(View view, FlexibleAdapter adapter) {
            super(view, adapter);

            avatarView = (ImageView) view.findViewById(R.id.avatar);
            moreCountView = (OpenSansTextView) view.findViewById(R.id.moreCount);
            text1View = (OpenSansTextView) view.findViewById(R.id.text1);
            text2View = (OpenSansTextView) view.findViewById(R.id.text2);
            moreView = (ImageView) view.findViewById(R.id.more);
        }
    }
}

Fragment (the rest of the view is instantiated in onCreateView

@Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        FlexibleAdapter.enableLogs(true);

        Context context = getContext();

        if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CALL_LOG) == PackageManager.PERMISSION_GRANTED) {
            CallLogUtils.getAllCallsFromLog(context, null);// use null so its not a recursive call to this function
        }

        SimpleDateFormat timeFormatter = new SimpleDateFormat("h:mm a");
        SimpleDateFormat dateFormatter = new SimpleDateFormat("MMM d");

        HashMap<Integer, RecentContactsAdapter.LocalDataForRecentCall> localMap = new HashMap<>();
        HashMap<String, String> callLogToPhoneMap = new HashMap<>();

        List<GroupedRecentCall> groupedRecentCallList = ModelManager.getCallsGrouped(context);
        List<AbstractFlexibleItem> recentCallsWithHeaders = new ArrayList<>();

        RecentContactHeaderItem communcationStates = new RecentContactHeaderItem("Communication Stats");
        RecentContactHeaderItem frequentCallers = new RecentContactHeaderItem("Frequent callers");
        RecentContactHeaderItem recentActivity = new RecentContactHeaderItem("Recent activity");

        for (int i = 0; i < groupedRecentCallList.size(); i++) {
            GroupedRecentCall groupedRecentCall = groupedRecentCallList.get(i);
            RecentContactItem item = new RecentContactItem(context, (i < groupedRecentCallList.size() / 2) ? frequentCallers : recentActivity, timeFormatter,
                    dateFormatter, groupedRecentCall, localMap, callLogToPhoneMap);
            item.setHeader((i < groupedRecentCallList.size() / 2) ? frequentCallers : recentActivity);
            recentCallsWithHeaders.add(item);
        }

        adapter = new RecentContactsAdapter(context, recentCallsWithHeaders);

        recyclerView = (RecyclerView) getView().findViewById(R.id.list);
        recyclerView.setLayoutManager(new SmoothScrollLinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
        // load adapter here for < 4.1
        recyclerView.setAdapter(adapter);
        recyclerView.setHasFixedSize(true);

//        listView.setLayoutManager(new LinearLayoutManager(getContext()));
        recyclerView.setItemAnimator(new DefaultItemAnimator() {
            @Override
            public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder, List<Object> payloads) {
                return true;
            }
        });

        adapter.setDisplayHeadersAtStartUp(true);
        adapter.enableStickyHeaders();
}

Fragment layout

<?xml version="1.0" encoding="utf-8"?>
<!--- used by recent contacts and address book -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipeContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/list"
            android:background="@color/white"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </android.support.v4.widget.SwipeRefreshLayout>

    <!-- needed for FlexibleAdapter sticky headers -->
    <include layout="@layout/sticky_header_layout"/>

    <include
        android:id="@android:id/empty"
        android:visibility="gone"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        layout="@layout/empty_layout" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleSmall"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_centerInParent="true"
        android:visibility="gone"/>

    <spokeo.com.spokeomobile.views.OpenSansTextView
        android:id="@+id/progressText"
        android:layout_below="@id/progressBar"
        android:layout_marginTop="10dp"
        android:gravity="center"
        android:layout_centerHorizontal="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        android:text="Loading Connections"
        android:textColor="@color/dark_gray"
        android:visibility="gone"/>

</RelativeLayout>

example not using List

I am trying to think on how to integrate your lib (5.0.0).
In my use case getting a List to use with the adapter is really an options.
I hold a list of sections which each holds a list of items and a possible header.
I wondering if there might be a way to use the FlexibleAdapter by subclassing it to get the item from my sections.
It seems doable but i might have issues making it work with all the features:

  • getItemCount: that method is final which means i can't do getItemCount = sectionsCount*ItemCount
  • showAllHeaders / hideAllHeaders / isHeaderShared seems to rely on mItems
  • filter / expand ... seems also to rely on mItems

I think this might require some refactoring to do things like this.
I also think it would be doable without changing anything performance wise.
For example:

public List<Integer> getExpandedPositions() {
        List<Integer> expandedPositions = new ArrayList<Integer>();
        for (int i = 0; i < mItems.size() - 1; i++) {
            T item = mItems.get(i);
            if (isExpanded(item))
                expandedPositions.add(i);
        }
        return expandedPositions;
    }

would become

public List<Integer> getExpandedPositions() {
        List<Integer> expandedPositions = new ArrayList<Integer>();
        for (int i = 0; i < getItemCount() - 1; i++) {
            T item = getItem(i);
            if (isExpanded(item))
                expandedPositions.add(i);
        }
        return expandedPositions;
    }

What do you think?

FYI i am trying to integrate your lib in my fork of the Titanium Mobile framework

Add a FlexibleViewHolder

I was think about add a new utility class FlexibleViewHolder it will help developers it alwasy need to implements the View.OnClickListener and View.OnLongClickListener

public abstract class FlexibleViewHolder extends RecyclerView.ViewHolder
        implements View.OnClickListener, View.OnLongClickListener {

    private final FlexibleAdapter adapter;
    private final OnListItemClickListener onListItemClickListener;

    public FlexibleViewHolder(View itemView,FlexibleAdapter adapter,
                              OnListItemClickListener onListItemClickListener) {
        super(itemView);
        this.adapter = adapter;

        this.onListItemClickListener = onListItemClickListener;

        this.itemView.setOnClickListener(this);
        this.itemView.setOnLongClickListener(this);
    }

    @Override
    public void onClick(View view) {
        if (onListItemClickListener.onListItemClick(getAdapterPosition())) {
            toggleActivation();
        }
    }

    @Override
    public boolean onLongClick(View view) {
        onListItemClickListener.onListItemLongClick(getAdapterPosition());
        toggleActivation();
        return true;
    }

    protected void toggleActivation() {
        itemView.setActivated(adapter.isSelected(getAdapterPosition()));
    }

    public interface OnListItemClickListener {
        boolean onListItemClick(int position);
        void onListItemLongClick(int position);
    }

}

So you can easily override the toggleActivation to add whatever you want and implements the OnListItemClickListener on your Activiy/Fragment

Bug: demo is weird when searching

  1. The top-left corner has a "..." button which does nothing
  2. Pressing the X button on the right closes the search, instead of just clearing the query
  3. pressing the back button (of the nav buttons) doesn't close the search, but instead it closes the activity.

Review the example App

Add configuration Dialog or load new Activity to show all the power the library has.
Separate example with new Activities/Fragments, Sections with sticky headers, Expandable items, Expandable headers (to evaluate), Linear/Grid animations, Helpers use cases.

Adjust and improve Wiki pages

Add a Wiki page with all functionalities and quick description of what the library can do.
Add and adjust Wiki pages with tutorials for v5.0.0 to show the main functionalities.
Add a side menu for different versions.

Status of the Wiki pages

The basics

  • Library structure
  • Setting Up
  • Setting Up Advanced
  • Item interfaces
  • ViewHolders
  • Callbacks
  • Migrations

Functionalities

  • Update Data Set
  • Selection modes
  • Headers and Sections
  • Scrollable Headers and Footers
  • Expandable items
  • Drag&Drop and Swipe
  • Search Filter
  • EndlessScroll / On Load More
  • Adapter Animations

Extensions

  • Smooth Layout Managers
  • Thirds Layout Managers
  • Flexible Item Decoration
  • FastScroller
  • Utils
  • ActionModeHelper
  • UndoHelper
  • AnimatorHelper
  • Payload

Demo App

  • Activity & Fragments

* = Initiated / Not completed

Sections hide/show

Special items can be identified as sections (such expandables or headers), they can be hidden or shown programmatically, adapter will reflect the change.

Simplify constructors to handle multiple listeners at once

Simplify ALL constructors. The adapter should recognize which type of listener an Activity or a Fragment is implementing the events triggered by the User or by the adapter.
Give the possibility to Developers to decide which listeners to implement one by one.

Maybe pass 1 single parameter to represent all listeners that object belongs to, or use prefix-with methodology to set the listeners at startup.

Make items expandable

Expandable and Not expandable items should have selection coherence, meaning that is not possible to select both View types.
Filtering should automatically expand the expandable if searchText exists in his subItems.

Sorting items?

Is there a way to sort items without inserting them at the correct position?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.