diff --git a/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowLayoutNode.java b/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowLayoutNode.java index 023f2e4a..b1da8dcb 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowLayoutNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowLayoutNode.java @@ -20,6 +20,7 @@ import android.text.TextUtils; import android.view.View; import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.StaggeredGridLayoutManager; @@ -28,6 +29,9 @@ import com.github.pengfeizhou.jscore.JSONBuilder; import com.github.pengfeizhou.jscore.JSObject; import com.github.pengfeizhou.jscore.JSValue; +import org.json.JSONArray; +import org.json.JSONObject; + import java.util.HashSet; import java.util.Set; import java.util.concurrent.Callable; @@ -36,6 +40,7 @@ import pub.doric.DoricContext; import pub.doric.DoricScrollChangeListener; import pub.doric.IDoricScrollable; import pub.doric.async.AsyncResult; +import pub.doric.extension.bridge.DoricMethod; import pub.doric.extension.bridge.DoricPlugin; import pub.doric.shader.SuperNode; import pub.doric.shader.ViewNode; @@ -326,6 +331,53 @@ public class FlowLayoutNode extends SuperNode implements IDoricScr listeners.remove(listener); } + private int calibratePosition(int position) { + if (hasHeader() && position == 0) { + return -11; + } + if (hasFooter() && position == this.itemCount + + (this.loadMore ? 1 : 0) + + (this.hasHeader() ? 1 : 0) + + (this.hasFooter() ? 1 : 0) - 1) { + return -12; + } + if (position >= this.itemCount + (this.hasHeader() ? 1 : 0)) { + return -10; + } + if (this.hasHeader()) { + return position - 1; + } + return position; + } + + @DoricMethod + public JSONArray findVisibleItems() { + int[] startPos = staggeredGridLayoutManager.findFirstVisibleItemPositions(null); + int[] endPos = staggeredGridLayoutManager.findLastVisibleItemPositions(null); + JSONArray jsonArray = new JSONArray(); + for (int i = 0; i < staggeredGridLayoutManager.getSpanCount(); i++) { + jsonArray.put(new JSONBuilder() + .put("first", calibratePosition(startPos[i])) + .put("last", calibratePosition(endPos[i])) + .toJSONObject()); + } + return jsonArray; + } + + @DoricMethod + public JSONArray findCompletelyVisibleItems() { + int[] startPos = staggeredGridLayoutManager.findFirstCompletelyVisibleItemPositions(null); + int[] endPos = staggeredGridLayoutManager.findFirstCompletelyVisibleItemPositions(null); + JSONArray jsonArray = new JSONArray(); + for (int i = 0; i < staggeredGridLayoutManager.getSpanCount(); i++) { + jsonArray.put(new JSONBuilder() + .put("first", calibratePosition(startPos[i])) + .put("last", calibratePosition(endPos[i])) + .toJSONObject()); + } + return jsonArray; + } + boolean hasHeader() { return !TextUtils.isEmpty(this.headerViewId); } diff --git a/doric-android/doric/src/main/java/pub/doric/shader/list/ListNode.java b/doric-android/doric/src/main/java/pub/doric/shader/list/ListNode.java index da300d68..7189c784 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/list/ListNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/list/ListNode.java @@ -26,12 +26,15 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.github.pengfeizhou.jscore.JSArray; import com.github.pengfeizhou.jscore.JSDecoder; import com.github.pengfeizhou.jscore.JSNumber; import com.github.pengfeizhou.jscore.JSONBuilder; import com.github.pengfeizhou.jscore.JSObject; import com.github.pengfeizhou.jscore.JSValue; +import org.json.JSONObject; + import java.util.HashSet; import java.util.Set; import java.util.concurrent.Callable; @@ -300,6 +303,47 @@ public class ListNode extends SuperNode implements IDoricScrollabl moveToPosition(pos.toInt(), animated); } + private int calibratePosition(int position) { + if (hasHeader() && position == 0) { + return -11; + } + if (hasFooter() && position == this.itemCount + + (this.loadMore ? 1 : 0) + + (this.hasHeader() ? 1 : 0) + + (this.hasFooter() ? 1 : 0) - 1) { + return -12; + } + if (position >= this.itemCount + (this.hasHeader() ? 1 : 0)) { + return -10; + } + if (this.hasHeader()) { + return position - 1; + } + return position; + } + + @DoricMethod + public JSONObject findVisibleItems() { + LinearLayoutManager linearLayoutManager = (LinearLayoutManager) this.mView.getLayoutManager(); + assert linearLayoutManager != null; + int startPos = linearLayoutManager.findFirstVisibleItemPosition(); + int endPos = linearLayoutManager.findLastVisibleItemPosition(); + return new JSONBuilder() + .put("first", calibratePosition(startPos)) + .put("last", calibratePosition(endPos)).toJSONObject(); + } + + @DoricMethod + public JSONObject findCompletelyVisibleItems() { + LinearLayoutManager linearLayoutManager = (LinearLayoutManager) this.mView.getLayoutManager(); + assert linearLayoutManager != null; + int startPos = linearLayoutManager.findFirstCompletelyVisibleItemPosition(); + int endPos = linearLayoutManager.findLastCompletelyVisibleItemPosition(); + return new JSONBuilder() + .put("first", calibratePosition(startPos)) + .put("last", calibratePosition(endPos)).toJSONObject(); + } + private void moveToPosition(int pos, boolean smooth) { if (mView.getLayoutManager() == null) { return; diff --git a/doric-js/src/widget/flowlayout.ts b/doric-js/src/widget/flowlayout.ts index bd406fc0..6f65718f 100644 --- a/doric-js/src/widget/flowlayout.ts +++ b/doric-js/src/widget/flowlayout.ts @@ -16,6 +16,7 @@ import { Stack } from './layouts' import { Property, Superview, View, NativeViewModel } from '../ui/view' import { layoutConfig } from '../util/index.util' +import { BridgeContext } from '../..' export class FlowLayoutItem extends Stack { /** @@ -94,6 +95,21 @@ export class FlowLayout extends Superview { @Property footer?: FlowLayoutItem + /** + * @param context + * @returns Returns the range of the visible views for each column. + */ + findVisibleItems(context: BridgeContext) { + return this.nativeChannel(context, 'findVisibleItems')() as Promise<{ first: number, last: number }[]> + } + /** + * @param context + * @returns Returns the range of the completely visible views for each column. + */ + findCompletelyVisibleItems(context: BridgeContext) { + return this.nativeChannel(context, 'findCompletelyVisibleItems')() as Promise<{ first: number, last: number }[]> + } + reset() { this.cachedViews.clear() diff --git a/doric-js/src/widget/list.ts b/doric-js/src/widget/list.ts index 7c28f7f9..6dce3e15 100644 --- a/doric-js/src/widget/list.ts +++ b/doric-js/src/widget/list.ts @@ -35,6 +35,12 @@ export class ListItem extends Stack { }[] } +export enum OtherItems { + LoadMore = -10, + Header = -11, + Footer = -12, +} + export class List extends Superview { private cachedViews: Map = new Map @@ -97,6 +103,20 @@ export class List extends Superview { const animated = config?.animated return this.nativeChannel(context, 'scrollToItem')({ index, animated, }) as Promise } + /** + * @param context + * @returns Returns the range of the visible views. + */ + findVisibleItems(context: BridgeContext) { + return this.nativeChannel(context, 'findVisibleItems')() as Promise<{ first: number, last: number }> + } + /** + * @param context + * @returns Returns the range of the completely visible views. + */ + findCompletelyVisibleItems(context: BridgeContext) { + return this.nativeChannel(context, 'findCompletelyVisibleItems')() as Promise<{ first: number, last: number }> + } reset() { this.cachedViews.clear()