Back to Blog Home
← all posts

Harness the power of CollectionView (Part 2) - Swipeable cells for Android

May 17, 2023 — by Nathan Walker

This is Part 2 in a series highlighting CollectionView, a super powered list control with recycled rows providing you best of class on each platform suited to fit all sorts of needs.

  • → Part 2 - Swipeable cells for Android

👉 Demo Repo

Continuing onward with Swipeable cells for Android, we can again take advantage of layoutStyle to register behaviors to our control.

Harness layoutStyle to customize behavior

CollectionView supports a layoutStyle property allowing you to register custom platform behavior against the control, for example:

<CollectionView layoutStyle="swipe" />

This can be any string value used to identify a custom registered layout style:

CollectionView.registerLayoutStyle('swipe', {
  createLayout: () => {
    // return a customized platform layout
    return layout;
  },
});

The layouts possible on Android can be anything in the RecyclerView.LayoutManager family. It provides an abstract base class for variety of layouts like GridLayoutManager, LinearLayoutManager and StaggeredGridLayoutManager.

Numerous tutorials can be found covering this; here's just a few:

One of the neatest things about NativeScript is the documentation and tutorials for a lot of the things you may want to do are already available in variety of platform sources because after all, NativeScript gives you the platform in JavaScript!

Implement GridLayoutManager for swipeable cells

We can create one right from within our TypeScript codebase:

// Convenient Java/Kotlin package usage with NativeScript
import GridLayoutManager = androidx.recyclerview.widget.GridLayoutManager;

// Construct a GridLayoutManager
new GridLayoutManager(Utils.android.getApplicationContext(), 1);

Using the tutorials mentioned above as our guide we will implement 1 trailing swipe action by configuring the callbacks as illustrated. This is what we want our createLayout to look like:

CollectionView.registerLayoutStyle("swipe", {
  createLayout(view: CollectionView) {
    const recyclerView = <RecyclerView>view.nativeView;

    // Create a swipe to delete handler
    const swipeToDeleteCallback = SwipeToDeleteCallback.initWithOwner(
      new WeakRef(view)
    );

    // Register the handler with our RecyclerView
    const itemTouchHelper = new ItemTouchHelper(swipeToDeleteCallback);
    itemTouchHelper.attachToRecyclerView(recyclerView);

    // Upon a full swipe, add a nice animation when the row removes itself
    recyclerView.addItemDecoration(
      new SwipeToDeleteAnimation()
    );

    return new GridLayoutManager(Utils.android.getApplicationContext(), 1);
  }
})

Implement ItemTouchHelper

It's pretty neat to take Kotlin/Java code into a TypeScript file for usage with NativeScript. We can do so using the example blog posts to end up with this, in part:

import ItemTouchHelper = androidx.recyclerview.widget.ItemTouchHelper;
import RecyclerView = androidx.recyclerview.widget.RecyclerView;
import Canvas = android.graphics.Canvas;

@NativeClass()
class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback {

  // Common way to statically init a class with a WeakRef to the owner
  static initWithOwner(owner: WeakRef<CollectionView>) {
    const swipe = new SwipeToDeleteCallback(
      ItemTouchHelper.UP |
        ItemTouchHelper.DOWN |
        ItemTouchHelper.START |
        ItemTouchHelper.END,
      ItemTouchHelper.LEFT
    );
    swipe.owner = owner;
    return swipe;
  }

  onChildDraw(
    c: Canvas,
    recyclerView: RecyclerView,
    viewHolder: RecyclerView.ViewHolder,
    dX: number,
    dY: number,
    actionState: number,
    isCurrentlyActive: boolean
  ) {
    // configure swipeable regions as needed using standard drawable methods
  }
}

The full example illustrates how much is possible giving you full control over every nuanced aspect of what you'd like your target platform to do.

If we were to run this on Android (ns debug android) with CollectionView markup, styled with TailwindCSS, to match what we had with Part 1 as follows:

<CollectionView [items]="items" layoutStyle="swipe">
  <ng-template let-item="item">
    <GridLayout rows="auto,auto,auto" class="pl-4 pr-2 py-4 v-center">
      <Label
        [text]="item.name"
        class="text-[17px] text-black font-bold align-middle"
      ></Label>
      <Label
        row="1"
        [text]="item.subject"
        class="text-base text-black leading-none align-middle"
      ></Label>
      <Label
        row="2"
        [text]="item.body"
        class="text-base leading-none text-gray-500"
        maxLines="2"
      ></Label>
      <GridLayout rowSpan="3" columns="auto,auto" class="align-top h-right">
        <Label
          [text]="item.date | date : 'shortDate'"
          class="text-sm align-middle text-gray-500"
        ></Label>
      </GridLayout>
    </GridLayout>
  </ng-template>
</CollectionView>

We would see this:

At this point, you could take things further with additional swipe regions based on project needs.

We will look at "View Modes" next providing us the ability to switch between multi column layouts or single spanned row setups.

About nStudio

Founded in 2016 by open source collaborators, nStudio is recognized for establishing a healthy open source governance model to serve the global communities interest around NativeScript. If you are in need of professional assistance on your project, nStudio offers services spanning multiple disciplines and can be reached at [email protected].