Skip to content

Commit b742d08

Browse files
committed
feat(android): smooth scrolling to position now allows to choose snap to top or bottom
1 parent fd8a4aa commit b742d08

File tree

7 files changed

+68
-28
lines changed

7 files changed

+68
-28
lines changed

demo-snippets/vue3/SimpleGridsScrollToIndex.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import { ObservableArray } from '@nativescript/core';
2727
import { ref } from "nativescript-vue"
2828
import type { CollectionView } from 'src/collectionview';
29+
import { SnapPosition } from '@nativescript-community/ui-collectionview';
2930
3031
const itemList = ref(new ObservableArray([
3132
{ index: 0, name: 'TURQUOISE', color: '#1abc9c' },
@@ -63,7 +64,7 @@ function onItemTap({ index, item }) {
6364
}
6465
6566
function goToIndex() {
66-
collectionView.scrollToIndex(nextIndex.value, true);
67+
collectionView.scrollToIndex(nextIndex.value, true, SnapPosition.END);
6768
nextIndex.value = getNextIndex();
6869
}
6970

packages/collectionview/platforms/android/java/com/nativescript/collectionview/RecyclerView.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@
44
import android.view.View;
55
import android.view.ViewGroup;
66
import android.util.Log;
7+
import androidx.recyclerview.widget.LinearSmoothScroller;
78

89
public class RecyclerView extends androidx.recyclerview.widget.RecyclerView {
910
static final String TAG = "RecyclerView";
1011
public SizeChangedListener sizeChangedListener = null;
12+
protected com.nativescript.collectionview.SmoothScroller smoothScroller;
1113
public RecyclerView(Context context) {
1214
this(context, null);
1315
}
1416
public RecyclerView(Context context, android.util.AttributeSet attrs) {
1517
super(context, attrs);
18+
smoothScroller = new com.nativescript.collectionview.SmoothScroller(context);
1619
setHorizontalScrollBarEnabled(true);
1720
setVerticalScrollBarEnabled(true);
1821
setHasFixedSize(true);
@@ -43,4 +46,14 @@ protected void onMeasure(int widthMeasureSpec,
4346
sizeChangedListener.onMeasure();
4447
}
4548
}
49+
50+
@Override
51+
public void smoothScrollToPosition(int position) {
52+
this.smoothScrollToPosition(position, LinearSmoothScroller.SNAP_TO_START);
53+
}
54+
public void smoothScrollToPosition(int position, int snapPosition) {
55+
smoothScroller.snapPreference = snapPosition;
56+
smoothScroller.setTargetPosition(position);
57+
getLayoutManager().startSmoothScroll(smoothScroller);
58+
}
4659
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.nativescript.collectionview;
2+
3+
import androidx.recyclerview.widget.LinearSmoothScroller;
4+
import android.content.Context;
5+
6+
public class SmoothScroller extends LinearSmoothScroller {
7+
8+
public int snapPreference = LinearSmoothScroller.SNAP_TO_START;
9+
10+
public SmoothScroller(Context context) {
11+
super(context);
12+
}
13+
14+
@Override protected int getVerticalSnapPreference() {
15+
return snapPreference;
16+
}
17+
@Override protected int getHorizontalSnapPreference() {
18+
return snapPreference;
19+
}
20+
21+
}

src/collectionview/index.android.ts

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class SimpleCallback extends androidx.recyclerview.widget.ItemTouchHelper.Simple
5050
this.startPosition = startPosition;
5151
}
5252
this.endPosition = endPosition;
53-
const owner = this.owner && this.owner.get();
53+
const owner = this.owner?.get();
5454
if (owner) {
5555
owner._reorderItemInSource(startPosition, endPosition);
5656
return true;
@@ -67,7 +67,7 @@ class SimpleCallback extends androidx.recyclerview.widget.ItemTouchHelper.Simple
6767
}
6868
if (!viewHolder) {
6969
// this is where we identify the end of the drag and call the end event
70-
const owner = this.owner && this.owner.get();
70+
const owner = this.owner?.get();
7171

7272
if (this.endPosition === -1) {
7373
this.endPosition = this.startPosition;
@@ -99,26 +99,13 @@ class LongPressGestureListenerImpl extends android.view.GestureDetector.SimpleOn
9999
return global.__native(this);
100100
}
101101
public onLongPress(motionEvent: android.view.MotionEvent): void {
102-
const owner = this._owner && this._owner.get();
102+
const owner = this._owner?.get();
103103
if (owner) {
104104
owner.onReorderLongPress(motionEvent);
105105
}
106106
}
107107
}
108108

109-
@NativeClass
110-
class SmoothScroller extends androidx.recyclerview.widget.LinearSmoothScroller {
111-
constructor() {
112-
super(Utils.android.getApplicationContext());
113-
return global.__native(this);
114-
}
115-
116-
public getVerticalSnapPreference() {
117-
return androidx.recyclerview.widget.LinearSmoothScroller.SNAP_TO_START;
118-
}
119-
}
120-
121-
let smoothScroller: androidx.recyclerview.widget.LinearSmoothScroller;
122109
let LayoutParams: typeof android.view.ViewGroup.LayoutParams;
123110

124111
const extraLayoutSpaceProperty = new Property<CollectionViewBase, number>({
@@ -133,6 +120,12 @@ const nestedScrollingEnabledProperty = new Property<CollectionViewBase, boolean>
133120
defaultValue: true,
134121
valueConverter: booleanConverter
135122
});
123+
124+
export enum SnapPosition {
125+
START = 0,
126+
END = 1
127+
}
128+
136129
export class CollectionView extends CollectionViewBase {
137130
public static DEFAULT_TEMPLATE_VIEW_TYPE = 0;
138131
public static CUSTOM_TEMPLATE_ITEM_TYPE = 1;
@@ -270,7 +263,6 @@ export class CollectionView extends CollectionViewBase {
270263

271264
nativeView.setItemAnimator(animator);
272265

273-
smoothScroller = new SmoothScroller();
274266
this.refresh();
275267
}
276268
public disposeNativeView() {
@@ -910,14 +902,13 @@ export class CollectionView extends CollectionViewBase {
910902
}
911903
return view.computeVerticalScrollOffset() / Utils.layout.getDisplayDensity();
912904
}
913-
public scrollToIndex(index: number, animated: boolean = true) {
905+
public scrollToIndex(index: number, animated: boolean = true, snap: SnapPosition = SnapPosition.START) {
914906
const view = this.nativeViewProtected;
915907
if (!view) {
916908
return;
917909
}
918910
if (animated) {
919-
smoothScroller.setTargetPosition(index);
920-
view.getLayoutManager().startSmoothScroll(smoothScroller);
911+
view.smoothScrollToPosition(index, snap);
921912
} else {
922913
view.scrollToPosition(index);
923914
}

src/collectionview/index.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ import { CollectionViewBase } from './index-common';
44
export * from './index-common';
55

66
export type Orientation = 'horizontal' | 'vertical';
7+
export enum SnapPosition {
8+
START = 0,
9+
END = 1
10+
}
711

812
export class CollectionView extends CollectionViewBase {
913
public scrollOffset: number;
1014
public refresh();
1115
public refreshVisibleItems();
1216
public isItemAtIndexVisible(index: number): boolean;
13-
public scrollToIndex(index: number, animated: boolean);
17+
public scrollToIndex(index: number, animated: boolean, snap?: SnapPosition = SnapPosition.START);
1418
public scrollToOffset(value: number, animation?: boolean);
1519
public getViewForItemAtIndex(index: number): View;
1620
// on iOS a view is dragged from its center by default

src/collectionview/index.ios.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ export const contentInsetAdjustmentBehaviorProperty = new Property<CollectionVie
5757
defaultValue: ContentInsetAdjustmentBehavior.Automatic
5858
});
5959

60+
export enum SnapPosition {
61+
START = -1, // = androidx.recyclerview.widget.LinearSmoothScroller.SNAP_TO_START,
62+
END = 1 // = androidx.recyclerview.widget.LinearSmoothScroller.SNAP_TO_END
63+
}
64+
6065
export class CollectionView extends CollectionViewBase {
6166
private _layout: UICollectionViewLayout;
6267
private _dataSource: CollectionViewDataSource;
@@ -626,12 +631,14 @@ export class CollectionView extends CollectionViewBase {
626631
get verticalOffsetY() {
627632
return this.nativeViewProtected?.contentOffset.y || 0;
628633
}
629-
public scrollToIndex(index: number, animated: boolean = true) {
630-
this.nativeViewProtected.scrollToItemAtIndexPathAtScrollPositionAnimated(
631-
NSIndexPath.indexPathForItemInSection(index, 0),
632-
this.orientation === 'vertical' ? UICollectionViewScrollPosition.Top : UICollectionViewScrollPosition.Left,
633-
animated
634-
);
634+
public scrollToIndex(index: number, animated: boolean = true, snap: SnapPosition = SnapPosition.START) {
635+
let scrollPosition = UICollectionViewScrollPosition.Top;
636+
if (this.orientation === 'vertical') {
637+
scrollPosition = snap === SnapPosition.START ? UICollectionViewScrollPosition.Top : UICollectionViewScrollPosition.Bottom;
638+
} else {
639+
scrollPosition = snap === SnapPosition.START ? UICollectionViewScrollPosition.Left : UICollectionViewScrollPosition.Right;
640+
}
641+
this.nativeViewProtected.scrollToItemAtIndexPathAtScrollPositionAnimated(NSIndexPath.indexPathForItemInSection(index, 0), scrollPosition, animated);
635642
}
636643

637644
scrollToOffset(value, animated) {

src/collectionview/typings/android.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ declare namespace com {
3838
static createRecyclerView(context): RecyclerView;
3939
static inflate(context): RecyclerView;
4040
sizeChangedListener?: SizeChangedListener;
41+
smoothScrollToPosition(position: number, snapPosition?: number);
4142
}
4243
export class SizeChangedListener {
4344
constructor(impl?: { onSizeChanged(w: number, h: number, oldw: number, oldh: number); onMeasure() });
@@ -68,6 +69,8 @@ declare namespace com {
6869
getSpanSize(param0: number): number;
6970
}
7071
}
72+
73+
export class SmoothScroller extends androidx.recyclerview.widget.LinearSmoothScroller {}
7174
}
7275
}
7376
}

0 commit comments

Comments
 (0)