!39387 RepeatVirtualScroll reRender optimization

Merge pull request !39387 from denis.pikalov/arkui-34-rerender-optim
This commit is contained in:
openharmony_ci 2024-08-09 01:23:15 +00:00 committed by Gitee
commit cb6244f9e3
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
16 changed files with 280 additions and 136 deletions

View File

@ -7453,27 +7453,29 @@ class ObserveV2 {
// clear any previously created dependency view model object to elmtId
// find these view model objects with the reverse map id2targets_
clearBinding(id) {
const targetSet = this.id2targets_[id];
let target;
if (targetSet && targetSet instanceof Set) {
targetSet.forEach((weakTarget) => {
var _a, _b;
if ((target = weakTarget.deref()) && target instanceof Object) {
const idRefs = target[ObserveV2.ID_REFS];
const symRefs = target[ObserveV2.SYMBOL_REFS];
if (idRefs) {
(_a = idRefs[id]) === null || _a === void 0 ? void 0 : _a.forEach(key => { var _a; return (_a = symRefs === null || symRefs === void 0 ? void 0 : symRefs[key]) === null || _a === void 0 ? void 0 : _a.delete(id); });
delete idRefs[id];
}
else {
for (let key in symRefs) {
(_b = symRefs[key]) === null || _b === void 0 ? void 0 : _b.delete(id);
}
;
}
var _a;
// multiple weakRefs might point to the same target - here we get Set of unique targets
const targetSet = new Set();
(_a = this.id2targets_[id]) === null || _a === void 0 ? void 0 : _a.forEach((weak) => {
if (weak.deref() instanceof Object) {
targetSet.add(weak.deref());
}
});
targetSet.forEach((target) => {
var _a, _b;
const idRefs = target[ObserveV2.ID_REFS];
const symRefs = target[ObserveV2.SYMBOL_REFS];
if (idRefs) {
(_a = idRefs[id]) === null || _a === void 0 ? void 0 : _a.forEach(key => { var _a; return (_a = symRefs === null || symRefs === void 0 ? void 0 : symRefs[key]) === null || _a === void 0 ? void 0 : _a.delete(id); });
delete idRefs[id];
}
else {
for (let key in symRefs) {
(_b = symRefs[key]) === null || _b === void 0 ? void 0 : _b.delete(id);
}
});
}
;
}
});
delete this.id2targets_[id];
delete this.id2cmp_[id];
@ -10126,8 +10128,18 @@ class __Repeat {
return this;
}
// function to decide which template to use, each template has an id
templateId(typeFunc) {
this.config.typeGenFunc = typeFunc;
templateId(typeGenFunc) {
// typeGenFunc wrapper with ttype validation
const typeGenFuncSafe = (item, index) => {
const itemType = typeGenFunc(item, index);
const itemFunc = this.config.itemGenFuncs[itemType];
if (typeof itemFunc != 'function') {
stateMgmtConsole.applicationError(`Repeat with virtual scroll. Missing Repeat.template for id '${itemType}'`);
return '';
}
return itemType;
};
this.config.typeGenFunc = typeGenFuncSafe;
return this;
}
// template: id + builder function to render specific type of data item
@ -10358,6 +10370,8 @@ class __RepeatVirtualScrollImpl {
this.repeatItem4Key_ = new Map();
// RepeatVirtualScrollNode elmtId
this.repeatElmtId_ = -1;
// Last known active range (as sparse array)
this.lastActiveRangeData_ = [];
}
render(config, isInitialRender) {
this.arr_ = config.arr;
@ -10473,7 +10487,6 @@ class __RepeatVirtualScrollImpl {
return result;
}; // const onGetKeys4Range
const onGetTypes4Range = (from, to) => {
var _a;
if (to > this.totalCount_ || to > this.arr_.length) {
stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId: ${this.repeatElmtId_}: onGetTypes4Range from ${from} to ${to} \
with data array length ${this.arr_.length}, totalCount=${this.totalCount_} \
@ -10490,13 +10503,7 @@ class __RepeatVirtualScrollImpl {
ViewStackProcessor.StartGetAccessRecordingFor(this.repeatElmtId_);
ObserveV2.getObserve().startRecordDependencies(owningView, this.repeatElmtId_, false);
for (let i = from; i <= to && i < this.arr_.length; i++) {
let ttype = (_a = this.typeGenFunc_(this.arr_[i], i)) !== null && _a !== void 0 ? _a : '';
if (!this.itemGenFuncs_[ttype]) {
stateMgmtConsole.applicationError(`Repeat with virtual scroll elmtId: ${this.repeatElmtId_}. Factory function .templateId returns template id '${ttype}'.` +
(ttype === '') ? 'Missing Repeat.each ' : `missing Repeat.template for id '${ttype}'` + '! Unrecoverable application error!');
// fallback to use .each function and try to continue the app with it.
ttype = '';
}
let ttype = this.typeGenFunc_(this.arr_[i], i);
result.push(ttype);
} // for
ObserveV2.getObserve().stopRecordDependencies();
@ -10504,36 +10511,68 @@ class __RepeatVirtualScrollImpl {
return result;
}; // const onGetTypes4Range
const onSetActiveRange = (from, to) => {
// make sparse copy of this.arr_
this.lastActiveRangeData_ = new Array(this.arr_.length);
for (let i = from; i <= to && i < this.arr_.length; i++) {
const item = this.arr_[i];
const ttype = this.typeGenFunc_(this.arr_[i], i);
this.lastActiveRangeData_[i] = { item, ttype };
}
};
RepeatVirtualScrollNative.create(this.totalCount_, Object.entries(this.templateOptions_), {
onCreateNode,
onUpdateNode,
onGetKeys4Range,
onGetTypes4Range
onGetTypes4Range,
onSetActiveRange
});
RepeatVirtualScrollNative.onMove(this.onMoveHandler_);
}
reRender() {
this.purgeKeyCache();
RepeatVirtualScrollNative.invalidateKeyCache(this.totalCount_);
}
initialRenderItem(repeatItem) {
var _a, _b;
// execute the itemGen function
const itemType = (_a = this.typeGenFunc_(repeatItem.item, repeatItem.index)) !== null && _a !== void 0 ? _a : '';
const itemFunc = (_b = this.itemGenFuncs_[itemType]) !== null && _b !== void 0 ? _b : this.itemGenFuncs_[''];
if (typeof itemFunc === 'function') {
itemFunc(repeatItem);
if (this.hasVisibleItemsChanged()) {
this.purgeKeyCache();
RepeatVirtualScrollNative.updateRenderState(this.totalCount_, true);
}
else {
stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId ${this.repeatElmtId_}: ` +
(itemType === '') ? 'Missing Repeat.each ' : `missing Repeat.template for id '${itemType}'` +
'! Unrecoverable application error!');
// avoid re-render when data pushed outside visible area
RepeatVirtualScrollNative.updateRenderState(this.totalCount_, false);
}
}
initialRenderItem(repeatItem) {
// execute the itemGen function
const itemType = this.typeGenFunc_(repeatItem.item, repeatItem.index);
const itemFunc = this.itemGenFuncs_[itemType];
itemFunc(repeatItem);
}
hasVisibleItemsChanged() {
var _a, _b;
let lastActiveRangeIndex = 0;
// has any item or ttype in the active range changed?
for (let i in this.lastActiveRangeData_) {
const oldItem = (_a = this.lastActiveRangeData_[+i]) === null || _a === void 0 ? void 0 : _a.item;
const oldType = (_b = this.lastActiveRangeData_[+i]) === null || _b === void 0 ? void 0 : _b.ttype;
const newItem = this.arr_[+i];
const newType = this.typeGenFunc_(this.arr_[+i], +i);
if (oldItem !== newItem) {
return true;
}
if (oldType !== newType) {
return true;
}
lastActiveRangeIndex = +i;
}
return false;
}
/**
* maintain: index <-> key mapping
* create new key from keyGen function if not in cache

View File

@ -82,7 +82,8 @@ bool ParseAndVerifyParams(const JSCallbackInfo& info)
auto handlers = JSRef<JSObject>::Cast(info[PARAM_HANDLERS]);
if (!handlers->GetProperty("onCreateNode")->IsFunction() || !handlers->GetProperty("onUpdateNode")->IsFunction() ||
!handlers->GetProperty("onGetKeys4Range")->IsFunction() ||
!handlers->GetProperty("onGetTypes4Range")->IsFunction()) {
!handlers->GetProperty("onGetTypes4Range")->IsFunction() ||
!handlers->GetProperty("onSetActiveRange")->IsFunction()) {
return false;
}
@ -166,19 +167,28 @@ void JSRepeatVirtualScroll::Create(const JSCallbackInfo& info)
return list;
};
auto onSetActiveRange = [execCtx = info.GetExecutionContext(), func = JSFUNC(handlers, "onSetActiveRange")](
uint32_t from, uint32_t to) -> void {
JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
auto params = ConvertToJSValues(from, to);
func->Call(JSRef<JSObject>(), params.size(), params.data());
};
RepeatVirtualScrollModel::GetInstance()->Create(
totalCount, templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range);
totalCount, templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range,
onSetActiveRange);
}
void JSRepeatVirtualScroll::InvalidateKeyCache(const JSCallbackInfo& info)
void JSRepeatVirtualScroll::UpdateRenderState(const JSCallbackInfo& info)
{
ACE_SCOPED_TRACE("RepeatVirtualScroll:InvalidateKeyCache");
TAG_LOGD(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll::InvalidateKeyCache");
if (!info[0]->IsNumber()) {
ACE_SCOPED_TRACE("RepeatVirtualScroll:UpdateRenderState");
TAG_LOGD(AceLogTag::ACE_REPEAT, "JSRepeatVirtualScroll::UpdateRenderState");
if (!info[0]->IsNumber() || !info[1]->IsBoolean()) {
return;
}
auto totalCount = info[0]->ToNumber<uint32_t>();
RepeatVirtualScrollModel::GetInstance()->InvalidateKeyCache(totalCount);
auto visibleItemsChanged = info[1]->ToBoolean();
RepeatVirtualScrollModel::GetInstance()->UpdateRenderState(totalCount, visibleItemsChanged);
}
void JSRepeatVirtualScroll::OnMove(const JSCallbackInfo& info)
@ -199,7 +209,7 @@ void JSRepeatVirtualScroll::JSBind(BindingTarget globalObj)
{
JSClass<JSRepeatVirtualScroll>::Declare("RepeatVirtualScrollNative");
JSClass<JSRepeatVirtualScroll>::StaticMethod("create", &JSRepeatVirtualScroll::Create);
JSClass<JSRepeatVirtualScroll>::StaticMethod("invalidateKeyCache", &JSRepeatVirtualScroll::InvalidateKeyCache);
JSClass<JSRepeatVirtualScroll>::StaticMethod("updateRenderState", &JSRepeatVirtualScroll::UpdateRenderState);
JSClass<JSRepeatVirtualScroll>::StaticMethod("onMove", &JSRepeatVirtualScroll::OnMove);
JSClass<JSRepeatVirtualScroll>::Bind<>(globalObj);
}

View File

@ -26,7 +26,7 @@ public:
static void JSBind(BindingTarget globalObj);
static void Create(const JSCallbackInfo& info);
static void InvalidateKeyCache(const JSCallbackInfo& info);
static void UpdateRenderState(const JSCallbackInfo& info);
static void OnMove(const JSCallbackInfo& info);
};

View File

@ -53,10 +53,11 @@ declare class RepeatVirtualScrollNative {
onUpdateNode: (fromKey: string, forIndex: number) => void;
onGetKeys4Range: (from: number, toNumber: number) => Array<string>;
onGetTypes4Range: (from: number, toNumber: number) => Array<string>;
onSetActiveRange: (from: number, to: number) => void;
}
): void;
// invalidate C++ side map index -> key
static invalidateKeyCache(totalCount : number): void;
// invalidate caches in C++ side, trigger render if needed
static updateRenderState(totalCount: number, visibleItemsChanged: boolean): void;
// drag and drop
static onMove(handler: (from: number, to: number) => void);
}

View File

@ -200,8 +200,19 @@ class __Repeat<T> implements RepeatAPI<T> {
}
// function to decide which template to use, each template has an id
public templateId(typeFunc: RepeatTypeGenFunc<T>): RepeatAPI<T> {
this.config.typeGenFunc = typeFunc;
public templateId(typeGenFunc: RepeatTypeGenFunc<T>): RepeatAPI<T> {
// typeGenFunc wrapper with ttype validation
const typeGenFuncSafe = (item: T, index: number): string => {
const itemType = typeGenFunc(item, index);
const itemFunc = this.config.itemGenFuncs[itemType];
if (typeof itemFunc != 'function') {
stateMgmtConsole.applicationError(`Repeat with virtual scroll. Missing Repeat.template for id '${itemType}'`);
return '';
}
return itemType;
};
this.config.typeGenFunc = typeGenFuncSafe;
return this;
}

View File

@ -42,6 +42,9 @@ class __RepeatVirtualScrollImpl<T> {
// RepeatVirtualScrollNode elmtId
private repeatElmtId_ : number = -1;
// Last known active range (as sparse array)
private lastActiveRangeData_: Array<{ item: T, ttype: string }> = [];
public render(config: __RepeatConfig<T>, isInitialRender: boolean): void {
this.arr_ = config.arr;
this.itemGenFuncs_ = config.itemGenFuncs;
@ -130,7 +133,6 @@ class __RepeatVirtualScrollImpl<T> {
}; // onUpdateNode
const onGetKeys4Range = (from: number, to: number): Array<string> => {
if (to > this.totalCount_ || to > this.arr_.length) {
stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId ${this.repeatElmtId_}: onGetKeys4Range from ${from} to ${to} \
with data array length ${this.arr_.length}, totalCount=${this.totalCount_} \
@ -197,13 +199,7 @@ class __RepeatVirtualScrollImpl<T> {
ObserveV2.getObserve().startRecordDependencies(owningView, this.repeatElmtId_, false);
for (let i = from; i <= to && i < this.arr_.length; i++) {
let ttype = this.typeGenFunc_(this.arr_[i], i) ?? '';
if (!this.itemGenFuncs_[ttype]) {
stateMgmtConsole.applicationError(`Repeat with virtual scroll elmtId: ${this.repeatElmtId_}. Factory function .templateId returns template id '${ttype}'.` +
(ttype === '') ? 'Missing Repeat.each ' : `missing Repeat.template for id '${ttype}'` + '! Unrecoverable application error!');
// fallback to use .each function and try to continue the app with it.
ttype = '';
}
let ttype = this.typeGenFunc_(this.arr_[i], i);
result.push(ttype);
} // for
ObserveV2.getObserve().stopRecordDependencies();
@ -213,13 +209,25 @@ class __RepeatVirtualScrollImpl<T> {
return result;
}; // const onGetTypes4Range
const onSetActiveRange = (from: number, to: number): void => {
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: onSetActiveRange(${from}, ${to})`);
// make sparse copy of this.arr_
this.lastActiveRangeData_ = new Array<{item: T, ttype: string}>(this.arr_.length);
for (let i = from; i <= to && i < this.arr_.length; i++) {
const item = this.arr_[i];
const ttype = this.typeGenFunc_(this.arr_[i], i);
this.lastActiveRangeData_[i] = { item, ttype };
}
}
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl(${this.repeatElmtId_}): initialRenderVirtualScroll`);
RepeatVirtualScrollNative.create(this.totalCount_, Object.entries(this.templateOptions_), {
onCreateNode,
onUpdateNode,
onGetKeys4Range,
onGetTypes4Range
onGetTypes4Range,
onSetActiveRange
});
RepeatVirtualScrollNative.onMove(this.onMoveHandler_);
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl(${this.repeatElmtId_}): initialRenderVirtualScroll`);
@ -227,22 +235,47 @@ class __RepeatVirtualScrollImpl<T> {
private reRender(): void {
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl(${this.repeatElmtId_}): reRender ...`);
this.purgeKeyCache();
RepeatVirtualScrollNative.invalidateKeyCache(this.totalCount_);
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl(${this.repeatElmtId_}): reRender - done`);
if (this.hasVisibleItemsChanged()) {
this.purgeKeyCache();
RepeatVirtualScrollNative.updateRenderState(this.totalCount_, true);
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: reRender - done.`);
} else {
// avoid re-render when data pushed outside visible area
RepeatVirtualScrollNative.updateRenderState(this.totalCount_, false);
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl: reRender (no changes in visible items) - done.`);
}
}
private initialRenderItem(repeatItem: __RepeatItemFactoryReturn<T>): void {
// execute the itemGen function
const itemType = this.typeGenFunc_(repeatItem.item, repeatItem.index) ?? '';
const itemFunc = this.itemGenFuncs_[itemType] ?? this.itemGenFuncs_[''];
if (typeof itemFunc === 'function') {
itemFunc(repeatItem);
} else {
stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId ${this.repeatElmtId_}: ` +
(itemType === '') ? 'Missing Repeat.each ' : `missing Repeat.template for id '${itemType}'` +
'! Unrecoverable application error!');
const itemType = this.typeGenFunc_(repeatItem.item, repeatItem.index);
const itemFunc = this.itemGenFuncs_[itemType];
itemFunc(repeatItem);
}
private hasVisibleItemsChanged(): boolean {
let lastActiveRangeIndex = 0;
// has any item or ttype in the active range changed?
for (let i in this.lastActiveRangeData_) {
const oldItem = this.lastActiveRangeData_[+i]?.item;
const oldType = this.lastActiveRangeData_[+i]?.ttype;
const newItem = this.arr_[+i];
const newType = this.typeGenFunc_(this.arr_[+i], +i);
if (oldItem !== newItem) {
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl.hasVisibleItemsChanged() i:#${i} item changed => true`);
return true;
}
if (oldType !== newType) {
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl.hasVisibleItemsChanged() i:#${i} ttype changed => true`);
return true;
}
lastActiveRangeIndex = +i;
}
stateMgmtConsole.debug(`__RepeatVirtualScrollImpl.hasVisibleItemsChanged() => false`);
return false;
}
/**

View File

@ -167,23 +167,27 @@ class ObserveV2 {
// clear any previously created dependency view model object to elmtId
// find these view model objects with the reverse map id2targets_
public clearBinding(id: number): void {
const targetSet: Set<WeakRef<Object>> = this.id2targets_[id];
let target: Object | undefined;
if (targetSet && targetSet instanceof Set) {
targetSet.forEach((weakTarget) => {
if ((target = weakTarget.deref()) && target instanceof Object) {
const idRefs: Object | undefined = target[ObserveV2.ID_REFS];
const symRefs: Object = target[ObserveV2.SYMBOL_REFS];
// multiple weakRefs might point to the same target - here we get Set of unique targets
const targetSet = new Set<Object>();
this.id2targets_[id]?.forEach((weak : WeakRef<Object>) => {
if (weak.deref() instanceof Object) {
targetSet.add(weak.deref())
}
});
if (idRefs) {
idRefs[id]?.forEach(key => symRefs?.[key]?.delete(id));
delete idRefs[id];
} else {
for (let key in symRefs) { symRefs[key]?.delete(id) };
}
}
});
}
targetSet.forEach((target) => {
const idRefs: Object | undefined = target[ObserveV2.ID_REFS];
const symRefs: Object = target[ObserveV2.SYMBOL_REFS];
if (idRefs) {
idRefs[id]?.forEach(key => symRefs?.[key]?.delete(id));
delete idRefs[id];
} else {
for (let key in symRefs) {
symRefs[key]?.delete(id);
};
}
});
delete this.id2targets_[id];
delete this.id2cmp_[id];

View File

@ -156,6 +156,7 @@ public:
* memorize last active range(s)
*/
void SetLastActiveRange(uint32_t from, uint32_t to);
std::pair<uint32_t, uint32_t> GetLastActiveRange() { return lastActiveRanges_[0]; };
// formatting internal structures to string for debug output
// and possibly in modified form for DFX in the future

View File

@ -39,8 +39,9 @@ public:
const std::function<void(uint32_t forIndex)>& onCreateNode,
const std::function<void(const std::string& fromKey, uint32_t forIndex)>& onUpdateNode,
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetKeys4Range,
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetTypes4Range) = 0;
virtual void InvalidateKeyCache(uint32_t totalCount) = 0;
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetTypes4Range,
const std::function<void(uint32_t from, uint32_t to)>& onSetActiveRange) = 0;
virtual void UpdateRenderState(uint32_t totalCount, bool visibleItemsChanged) = 0;
virtual void OnMove(std::function<void(int32_t, int32_t)>&& onMove) = 0;
private:

View File

@ -29,7 +29,8 @@ void RepeatVirtualScrollModelNG::Create(
const std::function<void(uint32_t forIndex)>& onCreateNode,
const std::function<void(const std::string& fromKey, uint32_t forIndex)>& onUpdateNode,
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetKeys4Range,
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetTypes4Range)
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetTypes4Range,
const std::function<void(uint32_t from, uint32_t to)>& onSetActiveRange)
{
ACE_SCOPED_TRACE("RepeatVirtualScrollModelNG::Create");
auto* stack = ViewStackProcessor::GetInstance();
@ -41,20 +42,20 @@ void RepeatVirtualScrollModelNG::Create(
onCreateNode,
onUpdateNode,
onGetKeys4Range,
onGetTypes4Range
);
onGetTypes4Range,
onSetActiveRange);
stack->Push(repeatNode);
stack->PopContainer();
}
void RepeatVirtualScrollModelNG::InvalidateKeyCache(uint32_t totalCount)
void RepeatVirtualScrollModelNG::UpdateRenderState(uint32_t totalCount, bool visibleItemsChanged)
{
auto* stack = ViewStackProcessor::GetInstance();
auto nodeId = stack->ClaimNodeId();
auto repeatNode = ElementRegister::GetInstance()->GetSpecificItemById<RepeatVirtualScrollNode>(nodeId);
CHECK_NULL_VOID(repeatNode);
repeatNode->UpdateTotalCount(totalCount);
repeatNode->InvalidateKeyCache();
repeatNode->UpdateRenderState(visibleItemsChanged);
}
void RepeatVirtualScrollModelNG::OnMove(std::function<void(int32_t, int32_t)>&& onMove)

View File

@ -35,9 +35,10 @@ public:
const std::function<void(uint32_t forIndex)>& onCreateNode,
const std::function<void(const std::string& fromKey, uint32_t forIndex)>& onUpdateNode,
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetKeys4Range,
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetTypes4Range) override;
const std::function<std::list<std::string>(uint32_t from, uint32_t to)>& onGetTypes4Range,
const std::function<void(uint32_t from, uint32_t to)>& onSetActiveRange) override;
void InvalidateKeyCache(uint32_t totalCount) override;
void UpdateRenderState(uint32_t totalCount, bool visibleItemsChanged) override;
void OnMove(std::function<void(int32_t, int32_t)>&& onMove) override;
};

View File

@ -33,7 +33,8 @@ RefPtr<RepeatVirtualScrollNode> RepeatVirtualScrollNode::GetOrCreateRepeatNode(i
const std::function<void(uint32_t)>& onCreateNode,
const std::function<void(const std::string&, uint32_t)>& onUpdateNode,
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetKeys4Range,
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range)
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range,
const std::function<void(uint32_t, uint32_t)>& onSetActiveRange)
{
auto node = ElementRegister::GetInstance()->GetSpecificItemById<RepeatVirtualScrollNode>(nodeId);
if (node) {
@ -42,7 +43,8 @@ RefPtr<RepeatVirtualScrollNode> RepeatVirtualScrollNode::GetOrCreateRepeatNode(i
return node;
}
node = MakeRefPtr<RepeatVirtualScrollNode>(
nodeId, totalCount, templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range);
nodeId, totalCount, templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range,
onSetActiveRange);
ElementRegister::GetInstance()->AddUINode(node);
return node;
@ -53,13 +55,22 @@ RepeatVirtualScrollNode::RepeatVirtualScrollNode(int32_t nodeId, int32_t totalCo
const std::function<void(uint32_t)>& onCreateNode,
const std::function<void(const std::string&, uint32_t)>& onUpdateNode,
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetKeys4Range,
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range)
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range,
const std::function<void(uint32_t, uint32_t)>& onSetActiveRange)
: ForEachBaseNode(V2::JS_REPEAT_ETS_TAG, nodeId), totalCount_(totalCount),
caches_(templateCachedCountMap, onCreateNode, onUpdateNode, onGetKeys4Range, onGetTypes4Range),
onSetActiveRange_(onSetActiveRange),
postUpdateTaskHasBeenScheduled_(false)
{
}
void RepeatVirtualScrollNode::UpdateTotalCount(uint32_t totalCount)
{
TAG_LOGD(AceLogTag::ACE_REPEAT, "UpdateTotalCount: %{public}d", totalCount);
totalCount_ = totalCount;
}
void RepeatVirtualScrollNode::DoSetActiveChildRange(
int32_t start, int32_t end,
int32_t cacheStart, int32_t cacheEnd)
@ -74,6 +85,8 @@ void RepeatVirtualScrollNode::DoSetActiveChildRange(
// memorize active range
caches_.SetLastActiveRange(start - cacheStart, end + cacheEnd);
// notify TS side
onSetActiveRange_(start, end);
bool needSync = caches_.RebuildL1([start, end, cacheStart, cacheEnd, this](
int32_t index, const RefPtr<UINode>& node) -> bool {
@ -105,8 +118,7 @@ void RepeatVirtualScrollNode::DoSetActiveChildRange(
((end < start) && (index <= end + cacheEnd || start - cacheStart <= index))) {
// keep in Repeat L1
TAG_LOGD(AceLogTag::ACE_REPEAT,
" ... in visible + pre-render range: index %{public}d -> nodeId %{public}d: "
"keep in Repeat L1",
" ... in visible + pre-render range: index %{public}d -> nodeId %{public}d: keep in Repeat L1",
static_cast<int32_t>(index), frameNode->GetId());
return true;
}
@ -116,8 +128,7 @@ void RepeatVirtualScrollNode::DoSetActiveChildRange(
"detach, move to spare items L2",
index, frameNode->GetId());
// move active node into L2 cached.
// check transition flag.
// move active node into L2 cached. check transition flag.
if (node->OnRemoveFromParent(true)) {
// OnRemoveFromParent returns true means the child can be removed from tree immediately.
RemoveDisappearingChild(node);
@ -165,6 +176,9 @@ void RepeatVirtualScrollNode::DropFromL1(const std::string& key)
void RepeatVirtualScrollNode::DoSetActiveChildRange(
const std::set<int32_t>& activeItems, const std::set<int32_t>& cachedItems, int32_t baseIndex)
{
// Notify TS side. Verify line below when DoSetActiveChildRange() will start to be used.
// Call onSetActiveRange_ here;
bool needSync =
caches_.RebuildL1([&activeItems, &cachedItems, baseIndex, this](int32_t index, RefPtr<UINode> node) -> bool {
if (node == nullptr) {
@ -197,19 +211,28 @@ void RepeatVirtualScrollNode::DoSetActiveChildRange(
}
}
void RepeatVirtualScrollNode::InvalidateKeyCache()
void RepeatVirtualScrollNode::UpdateRenderState(bool visibleItemsChanged)
{
TAG_LOGD(AceLogTag::ACE_REPEAT,
"InvalidateKeyCache triggered by Repeat rerender: nodeId: %{public}d .",
static_cast<int32_t>(GetId()));
// empty the cache index -> key
// C++ will need to ask all new keys from JS side
caches_.InvalidateKeyAndTTypeCaches();
children_.clear();
"UpdateRenderState triggered by Repeat rerender: nodeId: %{public}d, visibleItemsChanged: %{public}d",
static_cast<int32_t>(GetId()), visibleItemsChanged);
auto frameNode = GetParentFrameNode();
if (frameNode) {
frameNode->ChildrenUpdatedFrom(0);
if (visibleItemsChanged) {
// empty the cache index -> key
// C++ will need to ask all new keys from JS side
caches_.InvalidateKeyAndTTypeCaches();
children_.clear();
if (auto frameNode = GetParentFrameNode()) {
frameNode->ChildrenUpdatedFrom(0);
}
} else {
auto lastIndexInActiveRange = caches_.GetLastActiveRange().second;
TAG_LOGD(AceLogTag::ACE_REPEAT, "lastIndexInActiveRange:%{public}d", lastIndexInActiveRange);
if (auto frameNode = GetParentFrameNode()) {
frameNode->ChildrenUpdatedFrom(lastIndexInActiveRange + 1);
}
}
MarkNeedSyncRenderTree(true);

View File

@ -19,6 +19,7 @@
#include <cstdint>
#include <list>
#include <string>
#include <functional>
#include "base/memory/referenced.h"
#include "base/utils/macros.h"
@ -41,28 +42,27 @@ public:
const std::function<void(uint32_t)>& onCreateNode,
const std::function<void(const std::string&, uint32_t)>& onUpdateNode,
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetKeys4Range,
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range);
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range,
const std::function<void(uint32_t, uint32_t)>& onSetActiveRange);
RepeatVirtualScrollNode(int32_t nodeId, int32_t totalCount,
const std::map<std::string, std::pair<bool, uint32_t>>& templateCacheCountMap,
const std::function<void(uint32_t)>& onCreateNode,
const std::function<void(const std::string&, uint32_t)>& onUpdateNode,
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetKeys4Range,
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range);
const std::function<std::list<std::string>(uint32_t, uint32_t)>& onGetTypes4Range,
const std::function<void(uint32_t, uint32_t)>& onSetActiveRange);
~RepeatVirtualScrollNode() override = default;
void UpdateTotalCount(uint32_t totalCount)
{
totalCount_ = totalCount;
}
void UpdateTotalCount(uint32_t totalCount);
// Number of children that Repeat can product
// returns TotalCount
int32_t FrameCount() const override;
// called from TS upon Repeat rerender
void InvalidateKeyCache();
void UpdateRenderState(bool visibleItemsChanged);
/**
* GetChildren re-assembles children_ and cleanup the L1 cache
@ -185,6 +185,9 @@ private:
// caches:
mutable RepeatVirtualScrollCaches caches_;
// get active child range
std::function<void(uint32_t, uint32_t)> onSetActiveRange_;
// used by one of the unknown functions
std::list<std::string> ids_;

View File

@ -235,7 +235,9 @@ GridModelNG GridTestNg::CreateRepeatGrid(int32_t itemNumber, std::function<float
}
return keys;
};
repeatModel.Create(itemNumber, {}, createFunc, updateFunc, getKeys, getTypes);
std::function<void(uint32_t, uint32_t)> setActiveRange = [](uint32_t start, uint32_t end) {
};
repeatModel.Create(itemNumber, {}, createFunc, updateFunc, getKeys, getTypes, setActiveRange);
return model;
}

View File

@ -532,7 +532,9 @@ void ListTestNg::CreateRepeatVirtualScrollNode(int32_t itemNumber, const std::fu
}
return keys;
};
repeatModel.Create(itemNumber, {}, createFunc, updateFunc, getKeys, getTypes);
std::function<void(uint32_t, uint32_t)> setActiveRange = [](uint32_t start, uint32_t end) {
};
repeatModel.Create(itemNumber, {}, createFunc, updateFunc, getKeys, getTypes, setActiveRange);
}
void ListTestNg::FlushIdleTask(const RefPtr<ListPattern>& listPattern)

View File

@ -95,6 +95,12 @@ auto g_onGetTypes4Range = [](uint32_t from, uint32_t to) -> std::list<std::strin
return types;
};
/**
* Function needed by RepeatVirtualScrollNode constructor
*/
auto g_onSetActiveRange = [](uint32_t from, uint32_t to) {
};
/**
* Map needed by RepeatVirtualScrollCaches constructor
*/
@ -227,7 +233,8 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest005, TestSize.Level1)
g_onCreateNode,
g_onUpdateNode,
g_onGetKeys4Range,
g_onGetTypes4Range
g_onGetTypes4Range,
g_onSetActiveRange
);
stack->Push(repeatNode);
stack->PopContainer();
@ -252,7 +259,8 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest006, TestSize.Level1)
g_onCreateNode,
g_onUpdateNode,
g_onGetKeys4Range,
g_onGetTypes4Range
g_onGetTypes4Range,
g_onSetActiveRange
);
/**
@ -287,7 +295,8 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest007, TestSize.Level1)
g_onCreateNode,
g_onUpdateNode,
g_onGetKeys4Range,
g_onGetTypes4Range
g_onGetTypes4Range,
g_onSetActiveRange
);
/**
@ -316,7 +325,8 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest008, TestSize.Level1)
g_onCreateNode,
g_onUpdateNode,
g_onGetKeys4Range,
g_onGetTypes4Range
g_onGetTypes4Range,
g_onSetActiveRange
);
@ -336,7 +346,7 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest008, TestSize.Level1)
repeatNode->DoSetActiveChildRange(1, 2, 1, 1);
repeatNode->DoSetActiveChildRange(activeItems, cachedItems, 1);
repeatNode->DropFromL1("Key1");
repeatNode->InvalidateKeyCache();
repeatNode->UpdateRenderState(true);
repeatNode->RecycleItems(0, 100);
repeatNode->MoveData(0, 100);
@ -367,7 +377,8 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest009, TestSize.Level1)
g_onCreateNode,
g_onUpdateNode,
g_onGetKeys4Range,
g_onGetTypes4Range
g_onGetTypes4Range,
g_onSetActiveRange
);
/**
@ -648,7 +659,8 @@ HWTEST_F(RepeatNodeCacheSyntaxTest, RepeatNodeCacheTest021, TestSize.Level1)
g_onCreateNode,
g_onUpdateNode,
g_onGetKeys4Range,
g_onGetTypes4Range
g_onGetTypes4Range,
g_onSetActiveRange
);
ConfigurationChange cfgChange;