Skip to content

5.x | Setting Up Advanced

Davide Steduto edited this page May 1, 2018 · 26 revisions

In this page


Advanced usage

Improved Logs

FlexibleAdapter has an advanced log system that can be enabled to guide the developer during the initial configuration. The internal logger simplifies the use of android.util.Log by improving the call to all log methods by supplying arguments as parameters instead of creating a string. This avoids useless memory allocations when not requested:

  • the StringBuilder itself
  • the buffer
  • and the String object.

Enable logs before any instantiation by calling the static method.
Suggested central location is in Application.onCreate() where Timber©️ and other configurations are also configured.

import static eu.davidea.flexibleadapter.utils.Log.Level;

@Override
public void onCreate() {
    super.onCreate();
    FlexibleAdapter.enableLogs(Level.DEBUG); // Default is Level.SUPPRESS
}

The log methods check in advance if the level is enabled, while the string message with arguments is created if necessary. Others features are:

  • Custom TAG or Automatic TAG corresponding to the caller class name.
  • Runtime log level without the need of props file.
  • Method name with line number.

Custom Tags

You can distinguish the logs of several Adapters by setting a custom tag before each FlexibleAdapter instantiation, as following:

HomeActivity.java
FlexibleAdapter.useTag("MenuAdapter");
mAdapter = new FlexibleAdapter();
MessageListActivity.java
FlexibleAdapter.useTag("MsgAdapter");
mAdapter = new FlexibleAdapter();

Adapter configuration

FlexibleAdapter has many options that can be enabled/disabled at runtime. The options listed below are just a non-exhaustive example, for the full list with the explanations please refer to the API JavaDoc or the dedicated Wiki page per functionality.

// Compose the usual initial list
List<IFlexible> items = getDatabaseList();

// Initialize the Adapter with the callbacks event listener
FlexibleAdapter<IFlexible> adapter = new FlexibleAdapter<>(items, ObjectListener(1),
	stableIds(2)[true/false]);

// Non-exhaustive configuration that don't need RV instance
adapter.addListener(ObjectListener(1)) //Only if you didn't use the Constructor
    .expandItemsAtStartUp() //Items must be pre-initialized with expanded=true
    .setAutoCollapseOnExpand(true) //Force closes all others expandable item before expanding a new one
    .setMinCollapsibleLevel(int=1) //Auto collapse only items with level >= 1 (Don't auto-collapse level 0)
    .setAutoScrollOnExpand(true) //Needs a SmoothScrollXXXLayoutManager
    .setAnimationOnScrolling(true) //Enable scrolling animation: entry + forward scrolling
    .setAnimationOnReverseScrolling(true); //Enable animation for reverse scrolling
    //... and much more

// Initialize the RecyclerView and attach the Adapter to it as usual
recyclerView.setAdapter(adapter);

// (Optional) Configure the FastScroller
FastScroller fastScroller = (FastScroller) findViewById(R.id.fast_scroller);

// Non-exhaustive configuration that need RV instance
adapter.setFastScroller(fastScroller) //Add FastScroller
    .setLongPressDragEnabled(true) //Enable long press to drag items
    .setHandleDragEnabled(true) //Enable handle drag (handle view must be set in the VH)
    .setSwipeEnabled(true) //Enable swipe items
    .setDisplayHeadersAtStartUp(true) //Show Headers at startUp!
    .setStickyHeaders(true) //Make headers sticky (headers need to be shown)!
    .setEndlessScrollListener(listener, new ProgressItem()); //Endless scroll
    .setEndlessScrollThreshold(int=1) //Default=1
    //... and much more

1️⃣ = ObjectListener usually is the Activity or the Fragment, but it can be any object of your preference that handles all the event coming from different Adapters and ViewHolders. The wiki page Callbacks will show in the details the available listeners that you can implement.

2️⃣ = Having stable ids, when calling notifyDataSetChanged() through updateDataSet(newList, animate=false), will still run animation. Stable ids are more useful in big list and where pictures (loaded from network) are involved. However, in this library single notifications are the normality. More on stable ids? Check issue comment #230 or google it.

Extending FlexibleAdapter

FlexibleAdapter is not abstract anymore, but in case, you can always extend it to override and customize some methods.

public class MyAdapter extends FlexibleAdapter<IFlexible(3)> {

    public MyAdapter (List<IFlexible(3)> items, Object listener) {
        super(items, listener);
    }
    ...
}
public class FlexibleAdapter<T extends IFlexible(3)>
        extends AnimatorAdapter
        implements ItemTouchHelperCallback.AdapterCallback {
}

3️⃣ = Pay attention of the multiple item types the Adapter will handle: if you have more items to display, they all must eventually implement IFlexible as most common interface.
In the following example, the most common class is AbstractFlexibleItem so you can declare it in the class signature of MyAdapter:

public class HeaderItem extends AbstractHeaderItem<HeaderItem.HeaderViewHolder> {
}

public class SectionItem extends AbstractSectionableItem<SectionItem.ViewHolder,
        HeaderItem> {
}

// The Adapter signature is:
public class MyAdapter extends FlexibleAdapter<AbstractFlexibleItem> {
}

Where AbstractHeaderItem, AbstractSectionableItem and AbstractFlexibleItem (all explained in the next Wiki page) are defined as following:

public abstract class AbstractHeaderItem<VH extends FlexibleViewHolder>
        extends AbstractFlexibleItem<VH>
        implements IHeader<VH> {
}

public abstract class AbstractSectionableItem<VH extends RecyclerView.ViewHolder,
            H extends IHeader>
        extends AbstractFlexibleItem<VH>
        implements ISectionable<VH, H> {
}

public abstract class AbstractFlexibleItem<VH extends RecyclerView.ViewHolder>
        implements IFlexible<VH> {
}

As you see, IFlexible interface is inherited from the chain of the implementation of the abstract classes and the AbstractFlexibleItem class is the most common type.

Methods you might want to override

Basically all protected and public methods can be overridden, but here the most interesting ones:

addListener

/**
 * Initializes the listener(s) of this Adapter.
 * This method is automatically called from the Constructor.
 */
@CallSuper
public FlexibleAdapter addListener(@Nullable Object listener) {
    super.addListener(listener);
    // Your custom listeners here
    // Follow the same logic of the super method

    // Return this object
    return this;
}

onCreateBubbleText

/**
 * FastScroller value
 */
@Override
public String onCreateBubbleText(int position) {
    // Return the first letter of the item title / a specific date / a category.
    // See demoApp for suggestions.
}

onPostUpdate

/**
 * This method is called after the execution of Async Update and before the call to the
 * OnUpdateListener#onUpdateEmptyView(int).
 */
@Override
protected void onPostUpdate() {
    super.onPostUpdate();
    // Dedicated for user implementation
}

onPostFilter

/**
 * This method is called after the execution of Async Filter and before the call to the
 * OnUpdateListener#onUpdateEmptyView(int).
 */
@Override
protected void onPostFilter() {
    super.onPostFilter();
    // Dedicated for user implementation
}

isEmpty

/**
 * Define your own concept of "Empty".
 * This method is never called internally.
 */
public boolean isEmpty() {
    // super implementation:
    return getItemCount() == 0;
}

Callbacks

Callbacks (or Listeners) are better explained in the dedicated Callbacks Wiki page, however here some advanced implementation:

Custom listeners

You actually need to Cast the Adapter to your extended Adapter, you can apply it in the ViewHolder constructor OR in the createViewHolder() of the item, before passing the Adapter as parameter.
Suppose you have:

public class MyAdapter extends FlexibleAdapter  {
    public MyListener myListener;
}

In the item you should do:

@Override
MyViewHolder createViewHolder(View view, FlexibleAdapter adapter) {
    return new MyViewHolder(view, (MyAdapter) adapter); //Cast the adapter here
}
// ViewHolder Constructor
public MyViewHolder(View view, MyAdapter adapter) {
    super(view, adapter);
    adapter.myListener.onXXX();
}
// If you use the listener in the others VH methods you better override mAdapter
// field in the ViewHolder, so it becomes visible to all methods.

Receiving callbacks from different Adapters

Better solution is to use Fragment implementation, so each fragment can implement all the listener independently. But, if you cannot use fragments, you create 2 custom fields of the same type, so you can implement the method and assign it to the right adapter. That listener will be called only by that Adapter:

private FlexibleAdapter.OnItemClickListener clickListenerAdapter1 = 
        new FlexibleAdapter.OnItemClickListener() {
    @Override
    public boolean onItemClick(int position) {
        //...
        return false;
    }
};

mAdapter1.addListener(clickListenerAdapter1);

Repeat the previous code snippet for all callbacks coming from the 2nd Adapter.

Clone this wiki locally