feat:List add scrollToItem method

This commit is contained in:
pengfei.zhou 2020-04-22 11:18:21 +08:00 committed by osborn
parent 2541a13d74
commit 5f4942a8ff
12 changed files with 129 additions and 6 deletions

View File

@ -84,16 +84,22 @@ class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolder> {
return super.getItemViewType(position); return super.getItemViewType(position);
} }
private JSValue getItemModel(final int position) { private JSValue getItemModel(int position) {
if (position >= this.listNode.itemCount) { if (position >= this.listNode.itemCount) {
return this.listNode.getSubModel(this.listNode.loadMoreViewId); return this.listNode.getSubModel(this.listNode.loadMoreViewId);
} }
String id = listNode.itemValues.get(position); String id = listNode.itemValues.get(position);
if (TextUtils.isEmpty(id)) { if (TextUtils.isEmpty(id)) {
int batchCount = listNode.batchCount;
int start = position;
while (start > 0 && TextUtils.isEmpty(listNode.itemValues.get(start - 1))) {
start--;
batchCount++;
}
AsyncResult<JSDecoder> asyncResult = listNode.callJSResponse( AsyncResult<JSDecoder> asyncResult = listNode.callJSResponse(
"renderBunchedItems", "renderBunchedItems",
position, start,
listNode.batchCount); batchCount);
try { try {
JSDecoder jsDecoder = asyncResult.synchronous().get(); JSDecoder jsDecoder = asyncResult.synchronous().get();
JSValue result = jsDecoder.decode(); JSValue result = jsDecoder.decode();
@ -102,7 +108,7 @@ class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolder> {
for (int i = 0; i < jsArray.size(); i++) { for (int i = 0; i < jsArray.size(); i++) {
JSObject itemModel = jsArray.get(i).asObject(); JSObject itemModel = jsArray.get(i).asObject();
String itemId = itemModel.getProperty("id").asString().value(); String itemId = itemModel.getProperty("id").asString().value();
listNode.itemValues.put(i + position, itemId); listNode.itemValues.put(i + start, itemId);
listNode.setSubModel(itemId, itemModel); listNode.setSubModel(itemId, itemModel);
} }
return listNode.getSubModel(listNode.itemValues.get(position)); return listNode.getSubModel(listNode.itemValues.get(position));

View File

@ -24,6 +24,7 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.github.pengfeizhou.jscore.JSNumber;
import com.github.pengfeizhou.jscore.JSONBuilder; import com.github.pengfeizhou.jscore.JSONBuilder;
import com.github.pengfeizhou.jscore.JSObject; import com.github.pengfeizhou.jscore.JSObject;
import com.github.pengfeizhou.jscore.JSValue; import com.github.pengfeizhou.jscore.JSValue;
@ -36,6 +37,7 @@ import pub.doric.DoricContext;
import pub.doric.DoricScrollChangeListener; import pub.doric.DoricScrollChangeListener;
import pub.doric.IDoricScrollable; import pub.doric.IDoricScrollable;
import pub.doric.async.AsyncResult; import pub.doric.async.AsyncResult;
import pub.doric.extension.bridge.DoricMethod;
import pub.doric.extension.bridge.DoricPlugin; import pub.doric.extension.bridge.DoricPlugin;
import pub.doric.shader.SuperNode; import pub.doric.shader.SuperNode;
import pub.doric.shader.ViewNode; import pub.doric.shader.ViewNode;
@ -142,7 +144,7 @@ public class ListNode extends SuperNode<RecyclerView> implements IDoricScrollabl
} }
@Override @Override
protected void blend(RecyclerView view, String name, JSValue prop) { protected void blend(RecyclerView view, String name, final JSValue prop) {
switch (name) { switch (name) {
case "itemCount": case "itemCount":
if (!prop.isNumber()) { if (!prop.isNumber()) {
@ -188,6 +190,17 @@ public class ListNode extends SuperNode<RecyclerView> implements IDoricScrollabl
} }
this.onScrollEndFuncId = prop.asString().value(); this.onScrollEndFuncId = prop.asString().value();
break; break;
case "scrolledPosition":
if (!prop.isNumber()) {
return;
}
view.post(new Runnable() {
@Override
public void run() {
moveToPosition(prop.asNumber().toInt(), false);
}
});
break;
default: default:
super.blend(view, name, prop); super.blend(view, name, prop);
break; break;
@ -227,4 +240,49 @@ public class ListNode extends SuperNode<RecyclerView> implements IDoricScrollabl
public void removeScrollChangeListener(DoricScrollChangeListener listener) { public void removeScrollChangeListener(DoricScrollChangeListener listener) {
listeners.remove(listener); listeners.remove(listener);
} }
@DoricMethod
public void scrollToItem(JSObject params) {
boolean animated = false;
if (params.getProperty("animated").isBoolean()) {
animated = params.getProperty("animated").asBoolean().value();
}
JSNumber pos = params.getProperty("pos").asNumber();
moveToPosition(pos.toInt(), animated);
}
private void moveToPosition(int pos, boolean smooth) {
if (mView.getLayoutManager() == null) {
return;
}
if (!(getView().getLayoutManager() instanceof LinearLayoutManager)) {
defaultScrollTo(pos, smooth);
return;
}
LinearLayoutManager layoutManager = (LinearLayoutManager) mView.getLayoutManager();
int firstItem = layoutManager.findFirstVisibleItemPosition();
int lastItem = layoutManager.findLastVisibleItemPosition();
if (pos <= firstItem) {
defaultScrollTo(pos, smooth);
} else if (pos <= lastItem) {
// int top = getInnerView().getChildAt(pos - firstItem).getTop();
// if (smooth) {
// getInnerView().smoothScrollBy(0, top);
// } else {
// getInnerView().scrollBy(0, top);
// }
} else {
defaultScrollTo(pos, smooth);
}
}
private void defaultScrollTo(int pos, boolean b) {
if (b) {
mView.smoothScrollToPosition(pos);
} else {
mView.scrollToPosition(pos);
}
}
} }

View File

@ -107,6 +107,10 @@ - (void)blendView:(UITableView *)view forPropName:(NSString *)name propValue:(id
self.onScrollFuncId = prop; self.onScrollFuncId = prop;
} else if ([@"onScrollEnd" isEqualToString:name]) { } else if ([@"onScrollEnd" isEqualToString:name]) {
self.onScrollEndFuncId = prop; self.onScrollEndFuncId = prop;
} else if ([@"scrolledPosition" isEqualToString:name]) {
dispatch_async(dispatch_get_main_queue(), ^{
[view scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[prop unsignedIntegerValue] inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
});
} else { } else {
[super blendView:view forPropName:name propValue:prop]; [super blendView:view forPropName:name propValue:prop];
} }
@ -301,4 +305,10 @@ - (void)removeDidScrollBlock:(__nonnull DoricDidScrollBlock)didScrollListener {
[self.didScrollBlocks removeObject:didScrollListener]; [self.didScrollBlocks removeObject:didScrollListener];
} }
- (void)scrollToItem:(NSDictionary *)params {
BOOL animated = [params[@"animated"] boolValue];
NSUInteger scrolledPosition = [params[@"pos"] unsignedIntegerValue];
[self.view scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:scrolledPosition inSection:0] atScrollPosition:UITableViewScrollPositionNone animated:animated];
}
@end @end

View File

@ -1889,6 +1889,9 @@ var List = /** @class */ (function (_super) {
return this.cachedViews.values(); return this.cachedViews.values();
} }
}; };
List.prototype.scrollToItem = function (context, pos, animated) {
return this.nativeChannel(context, 'scrollToItem')({ pos: pos, animated: animated });
};
List.prototype.reset = function () { List.prototype.reset = function () {
this.cachedViews.clear(); this.cachedViews.clear();
this.itemCount = 0; this.itemCount = 0;
@ -1953,6 +1956,10 @@ var List = /** @class */ (function (_super) {
Property, Property,
__metadata$5("design:type", Function) __metadata$5("design:type", Function)
], List.prototype, "onScrollEnd", void 0); ], List.prototype, "onScrollEnd", void 0);
__decorate$5([
Property,
__metadata$5("design:type", Number)
], List.prototype, "scrolledPosition", void 0);
return List; return List;
}(Superview)); }(Superview));
function list(config) { function list(config) {

View File

@ -1395,6 +1395,9 @@ class List extends Superview {
return this.cachedViews.values(); return this.cachedViews.values();
} }
} }
scrollToItem(context, pos, animated) {
return this.nativeChannel(context, 'scrollToItem')({ pos, animated });
}
reset() { reset() {
this.cachedViews.clear(); this.cachedViews.clear();
this.itemCount = 0; this.itemCount = 0;
@ -1459,6 +1462,10 @@ __decorate$5([
Property, Property,
__metadata$5("design:type", Function) __metadata$5("design:type", Function)
], List.prototype, "onScrollEnd", void 0); ], List.prototype, "onScrollEnd", void 0);
__decorate$5([
Property,
__metadata$5("design:type", Number)
], List.prototype, "scrolledPosition", void 0);
function list(config) { function list(config) {
const ret = new List; const ret = new List;
for (let key in config) { for (let key in config) {

View File

@ -2854,6 +2854,9 @@ class List extends Superview {
return this.cachedViews.values(); return this.cachedViews.values();
} }
} }
scrollToItem(context, pos, animated) {
return this.nativeChannel(context, 'scrollToItem')({ pos, animated });
}
reset() { reset() {
this.cachedViews.clear(); this.cachedViews.clear();
this.itemCount = 0; this.itemCount = 0;
@ -2918,6 +2921,10 @@ __decorate$5([
Property, Property,
__metadata$5("design:type", Function) __metadata$5("design:type", Function)
], List.prototype, "onScrollEnd", void 0); ], List.prototype, "onScrollEnd", void 0);
__decorate$5([
Property,
__metadata$5("design:type", Number)
], List.prototype, "scrolledPosition", void 0);
function list(config) { function list(config) {
const ret = new List; const ret = new List;
for (let key in config) { for (let key in config) {

3
doric-js/index.d.ts vendored
View File

@ -499,6 +499,7 @@ declare module 'doric/lib/src/widget/image' {
declare module 'doric/lib/src/widget/list' { declare module 'doric/lib/src/widget/list' {
import { View, Superview, NativeViewModel } from "doric/lib/src/ui/view"; import { View, Superview, NativeViewModel } from "doric/lib/src/ui/view";
import { Stack } from "doric/lib/src/widget/layouts"; import { Stack } from "doric/lib/src/widget/layouts";
import { BridgeContext } from "doric/lib/src/runtime/global";
export class ListItem extends Stack { export class ListItem extends Stack {
/** /**
* Set to reuse native view * Set to reuse native view
@ -521,6 +522,8 @@ declare module 'doric/lib/src/widget/list' {
x: number; x: number;
y: number; y: number;
}) => void; }) => void;
scrolledPosition?: number;
scrollToItem(context: BridgeContext, pos: number, animated?: boolean): Promise<any>;
reset(): void; reset(): void;
isDirty(): boolean; isDirty(): boolean;
toModel(): NativeViewModel; toModel(): NativeViewModel;

View File

@ -1,5 +1,6 @@
import { View, Superview, NativeViewModel } from "../ui/view"; import { View, Superview, NativeViewModel } from "../ui/view";
import { Stack } from "./layouts"; import { Stack } from "./layouts";
import { BridgeContext } from "../runtime/global";
export declare class ListItem extends Stack { export declare class ListItem extends Stack {
/** /**
* Set to reuse native view * Set to reuse native view
@ -24,6 +25,8 @@ export declare class List extends Superview {
x: number; x: number;
y: number; y: number;
}) => void; }) => void;
scrolledPosition?: number;
scrollToItem(context: BridgeContext, pos: number, animated?: boolean): Promise<any>;
reset(): void; reset(): void;
private getItem; private getItem;
isDirty(): boolean; isDirty(): boolean;

View File

@ -47,6 +47,9 @@ export class List extends Superview {
return this.cachedViews.values(); return this.cachedViews.values();
} }
} }
scrollToItem(context, pos, animated) {
return this.nativeChannel(context, 'scrollToItem')({ pos, animated });
}
reset() { reset() {
this.cachedViews.clear(); this.cachedViews.clear();
this.itemCount = 0; this.itemCount = 0;
@ -111,6 +114,10 @@ __decorate([
Property, Property,
__metadata("design:type", Function) __metadata("design:type", Function)
], List.prototype, "onScrollEnd", void 0); ], List.prototype, "onScrollEnd", void 0);
__decorate([
Property,
__metadata("design:type", Number)
], List.prototype, "scrolledPosition", void 0);
export function list(config) { export function list(config) {
const ret = new List; const ret = new List;
for (let key in config) { for (let key in config) {

View File

@ -17,6 +17,7 @@
import { View, Property, Superview, NativeViewModel } from "../ui/view"; import { View, Property, Superview, NativeViewModel } from "../ui/view";
import { Stack } from "./layouts"; import { Stack } from "./layouts";
import { layoutConfig } from "../util/layoutconfig"; import { layoutConfig } from "../util/layoutconfig";
import { BridgeContext } from "../runtime/global";
export class ListItem extends Stack { export class ListItem extends Stack {
@ -63,6 +64,13 @@ export class List extends Superview {
@Property @Property
onScrollEnd?: (offset: { x: number, y: number }) => void onScrollEnd?: (offset: { x: number, y: number }) => void
@Property
scrolledPosition?: number
scrollToItem(context: BridgeContext, pos: number, animated?: boolean) {
return this.nativeChannel(context, 'scrollToItem')({ pos, animated }) as Promise<any>
}
reset() { reset() {
this.cachedViews.clear() this.cachedViews.clear()
this.itemCount = 0 this.itemCount = 0

View File

@ -2912,6 +2912,9 @@ class List extends Superview {
return this.cachedViews.values(); return this.cachedViews.values();
} }
} }
scrollToItem(context, pos, animated) {
return this.nativeChannel(context, 'scrollToItem')({ pos, animated });
}
reset() { reset() {
this.cachedViews.clear(); this.cachedViews.clear();
this.itemCount = 0; this.itemCount = 0;
@ -2976,6 +2979,10 @@ __decorate$5([
Property, Property,
__metadata$5("design:type", Function) __metadata$5("design:type", Function)
], List.prototype, "onScrollEnd", void 0); ], List.prototype, "onScrollEnd", void 0);
__decorate$5([
Property,
__metadata$5("design:type", Number)
], List.prototype, "scrolledPosition", void 0);
function list(config) { function list(config) {
const ret = new List; const ret = new List;
for (let key in config) { for (let key in config) {

File diff suppressed because one or more lines are too long