feat:add FlowLayout for Android
This commit is contained in:
parent
8a15297ebd
commit
6a34f45d44
@ -29,6 +29,8 @@ import pub.doric.refresh.RefreshableNode;
|
|||||||
import pub.doric.shader.HLayoutNode;
|
import pub.doric.shader.HLayoutNode;
|
||||||
import pub.doric.shader.ImageNode;
|
import pub.doric.shader.ImageNode;
|
||||||
import pub.doric.shader.ScrollerNode;
|
import pub.doric.shader.ScrollerNode;
|
||||||
|
import pub.doric.shader.flowlayout.FlowLayoutItemNode;
|
||||||
|
import pub.doric.shader.flowlayout.FlowLayoutNode;
|
||||||
import pub.doric.shader.list.ListItemNode;
|
import pub.doric.shader.list.ListItemNode;
|
||||||
import pub.doric.shader.list.ListNode;
|
import pub.doric.shader.list.ListNode;
|
||||||
import pub.doric.shader.RootNode;
|
import pub.doric.shader.RootNode;
|
||||||
@ -98,6 +100,8 @@ public class DoricRegistry {
|
|||||||
this.registerViewNode(SliderNode.class);
|
this.registerViewNode(SliderNode.class);
|
||||||
this.registerViewNode(SlideItemNode.class);
|
this.registerViewNode(SlideItemNode.class);
|
||||||
this.registerViewNode(RefreshableNode.class);
|
this.registerViewNode(RefreshableNode.class);
|
||||||
|
this.registerViewNode(FlowLayoutNode.class);
|
||||||
|
this.registerViewNode(FlowLayoutItemNode.class);
|
||||||
initRegistry(this);
|
initRegistry(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package pub.doric.shader.flowlayout;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.github.pengfeizhou.jscore.JSArray;
|
||||||
|
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||||
|
import com.github.pengfeizhou.jscore.JSNull;
|
||||||
|
import com.github.pengfeizhou.jscore.JSObject;
|
||||||
|
import com.github.pengfeizhou.jscore.JSValue;
|
||||||
|
|
||||||
|
import pub.doric.async.AsyncResult;
|
||||||
|
import pub.doric.shader.ViewNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: com.github.penfeizhou.doric.widget
|
||||||
|
* @Author: pengfei.zhou
|
||||||
|
* @CreateDate: 2019-11-12
|
||||||
|
*/
|
||||||
|
class FlowAdapter extends RecyclerView.Adapter<FlowAdapter.DoricViewHolder> {
|
||||||
|
|
||||||
|
private final FlowLayoutNode flowLayoutNode;
|
||||||
|
String renderItemFuncId;
|
||||||
|
int itemCount = 0;
|
||||||
|
int batchCount = 15;
|
||||||
|
SparseArray<String> itemValues = new SparseArray<>();
|
||||||
|
|
||||||
|
FlowAdapter(FlowLayoutNode flowLayoutNode) {
|
||||||
|
this.flowLayoutNode = flowLayoutNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public DoricViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
FlowLayoutItemNode node = (FlowLayoutItemNode) ViewNode.create(flowLayoutNode.getDoricContext(), "FlowLayoutItem");
|
||||||
|
node.init(flowLayoutNode);
|
||||||
|
return new DoricViewHolder(node, node.getNodeView());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull DoricViewHolder holder, int position) {
|
||||||
|
JSValue jsValue = getItemModel(position);
|
||||||
|
if (jsValue.isObject()) {
|
||||||
|
JSObject jsObject = jsValue.asObject();
|
||||||
|
holder.flowLayoutItemNode.setId(jsObject.getProperty("id").asString().value());
|
||||||
|
holder.flowLayoutItemNode.blend(jsObject.getProperty("props").asObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return itemCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
JSValue value = getItemModel(position);
|
||||||
|
if (value.isObject()) {
|
||||||
|
if (value.asObject().getProperty("identifier").isString()) {
|
||||||
|
return value.asObject().getProperty("identifier").asString().value().hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.getItemViewType(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSValue getItemModel(final int position) {
|
||||||
|
String id = itemValues.get(position);
|
||||||
|
if (TextUtils.isEmpty(id)) {
|
||||||
|
AsyncResult<JSDecoder> asyncResult = flowLayoutNode.callJSResponse(
|
||||||
|
"renderBunchedItems",
|
||||||
|
position,
|
||||||
|
batchCount);
|
||||||
|
try {
|
||||||
|
JSDecoder jsDecoder = asyncResult.synchronous().get();
|
||||||
|
JSValue result = jsDecoder.decode();
|
||||||
|
if (result.isArray()) {
|
||||||
|
JSArray jsArray = result.asArray();
|
||||||
|
for (int i = 0; i < jsArray.size(); i++) {
|
||||||
|
JSObject itemModel = jsArray.get(i).asObject();
|
||||||
|
String itemId = itemModel.getProperty("id").asString().value();
|
||||||
|
itemValues.put(i + position, itemId);
|
||||||
|
flowLayoutNode.setSubModel(itemId, itemModel);
|
||||||
|
}
|
||||||
|
return flowLayoutNode.getSubModel(itemValues.get(position));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return new JSNull();
|
||||||
|
} else {
|
||||||
|
JSObject childModel = flowLayoutNode.getSubModel(id);
|
||||||
|
if (childModel == null) {
|
||||||
|
return new JSNull();
|
||||||
|
} else {
|
||||||
|
return childModel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void blendSubNode(JSObject subProperties) {
|
||||||
|
for (int i = 0; i < itemValues.size(); i++) {
|
||||||
|
if (subProperties.getProperty("id").asString().value().equals(itemValues.valueAt(i))) {
|
||||||
|
notifyItemChanged(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DoricViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
FlowLayoutItemNode flowLayoutItemNode;
|
||||||
|
|
||||||
|
DoricViewHolder(FlowLayoutItemNode node, @NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
flowLayoutItemNode = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package pub.doric.shader.flowlayout;
|
||||||
|
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import com.github.pengfeizhou.jscore.JSObject;
|
||||||
|
import com.github.pengfeizhou.jscore.JSValue;
|
||||||
|
|
||||||
|
import pub.doric.DoricContext;
|
||||||
|
import pub.doric.extension.bridge.DoricPlugin;
|
||||||
|
import pub.doric.shader.StackNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: com.github.penfeizhou.doric.widget
|
||||||
|
* @Author: pengfei.zhou
|
||||||
|
* @CreateDate: 2019-11-12
|
||||||
|
*/
|
||||||
|
@DoricPlugin(name = "FlowLayoutItem")
|
||||||
|
public class FlowLayoutItemNode extends StackNode {
|
||||||
|
public String identifier = "";
|
||||||
|
|
||||||
|
public FlowLayoutItemNode(DoricContext doricContext) {
|
||||||
|
super(doricContext);
|
||||||
|
this.mReusable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void blend(FrameLayout view, String name, JSValue prop) {
|
||||||
|
if ("identifier".equals(name)) {
|
||||||
|
this.identifier = prop.asString().value();
|
||||||
|
} else {
|
||||||
|
super.blend(view, name, prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void blend(JSObject jsObject) {
|
||||||
|
super.blend(jsObject);
|
||||||
|
getNodeView().getLayoutParams().width = getLayoutParams().width;
|
||||||
|
getNodeView().getLayoutParams().height = getLayoutParams().height;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2019] [Doric.Pub]
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package pub.doric.shader.flowlayout;
|
||||||
|
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||||
|
|
||||||
|
import com.github.pengfeizhou.jscore.JSObject;
|
||||||
|
import com.github.pengfeizhou.jscore.JSValue;
|
||||||
|
|
||||||
|
import pub.doric.DoricContext;
|
||||||
|
import pub.doric.extension.bridge.DoricPlugin;
|
||||||
|
import pub.doric.shader.SuperNode;
|
||||||
|
import pub.doric.shader.ViewNode;
|
||||||
|
import pub.doric.utils.DoricUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Description: pub.doric.shader.flowlayout
|
||||||
|
* @Author: pengfei.zhou
|
||||||
|
* @CreateDate: 2019-11-28
|
||||||
|
*/
|
||||||
|
@DoricPlugin(name = "FlowLayout")
|
||||||
|
public class FlowLayoutNode extends SuperNode<RecyclerView> {
|
||||||
|
private final FlowAdapter flowAdapter;
|
||||||
|
private final StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(
|
||||||
|
2,
|
||||||
|
StaggeredGridLayoutManager.VERTICAL);
|
||||||
|
private int columnSpace = 0;
|
||||||
|
private int rowSpace = 0;
|
||||||
|
private final RecyclerView.ItemDecoration spacingItemDecoration = new RecyclerView.ItemDecoration() {
|
||||||
|
@Override
|
||||||
|
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
|
||||||
|
outRect.set(columnSpace / 2, rowSpace / 2, columnSpace / 2, rowSpace / 2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public FlowLayoutNode(DoricContext doricContext) {
|
||||||
|
super(doricContext);
|
||||||
|
this.flowAdapter = new FlowAdapter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ViewNode getSubNodeById(String id) {
|
||||||
|
RecyclerView.LayoutManager manager = mView.getLayoutManager();
|
||||||
|
if (manager == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < manager.getChildCount(); i++) {
|
||||||
|
View view = manager.getChildAt(i);
|
||||||
|
if (view == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
FlowAdapter.DoricViewHolder viewHolder = (FlowAdapter.DoricViewHolder) mView.getChildViewHolder(view);
|
||||||
|
if (id.equals(viewHolder.flowLayoutItemNode.getId())) {
|
||||||
|
return viewHolder.flowLayoutItemNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void blend(RecyclerView view, String name, JSValue prop) {
|
||||||
|
switch (name) {
|
||||||
|
case "columnSpace":
|
||||||
|
columnSpace = DoricUtils.dp2px(prop.asNumber().toFloat());
|
||||||
|
mView.setPadding(-columnSpace / 2, mView.getPaddingTop(), -columnSpace / 2, mView.getPaddingBottom());
|
||||||
|
break;
|
||||||
|
case "rowSpace":
|
||||||
|
rowSpace = DoricUtils.dp2px(prop.asNumber().toFloat());
|
||||||
|
mView.setPadding(mView.getPaddingLeft(), -rowSpace / 2, mView.getPaddingRight(), -rowSpace / 2);
|
||||||
|
break;
|
||||||
|
case "columnCount":
|
||||||
|
staggeredGridLayoutManager.setSpanCount(prop.asNumber().toInt());
|
||||||
|
break;
|
||||||
|
case "itemCount":
|
||||||
|
this.flowAdapter.itemCount = prop.asNumber().toInt();
|
||||||
|
break;
|
||||||
|
case "renderItem":
|
||||||
|
this.flowAdapter.renderItemFuncId = prop.asString().value();
|
||||||
|
// If reset renderItem,should reset native cache.
|
||||||
|
this.flowAdapter.itemValues.clear();
|
||||||
|
clearSubModel();
|
||||||
|
break;
|
||||||
|
case "batchCount":
|
||||||
|
this.flowAdapter.batchCount = prop.asNumber().toInt();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.blend(view, name, prop);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void blend(JSObject jsObject) {
|
||||||
|
super.blend(jsObject);
|
||||||
|
if (mView != null) {
|
||||||
|
mView.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
flowAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void blendSubNode(JSObject subProperties) {
|
||||||
|
String viewId = subProperties.getProperty("id").asString().value();
|
||||||
|
ViewNode node = getSubNodeById(viewId);
|
||||||
|
if (node != null) {
|
||||||
|
node.blend(subProperties.getProperty("props").asObject());
|
||||||
|
} else {
|
||||||
|
JSObject oldModel = getSubModel(viewId);
|
||||||
|
if (oldModel != null) {
|
||||||
|
recursiveMixin(subProperties, oldModel);
|
||||||
|
}
|
||||||
|
flowAdapter.blendSubNode(subProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RecyclerView build() {
|
||||||
|
RecyclerView recyclerView = new RecyclerView(getContext());
|
||||||
|
recyclerView.setLayoutManager(staggeredGridLayoutManager);
|
||||||
|
recyclerView.setAdapter(flowAdapter);
|
||||||
|
recyclerView.addItemDecoration(spacingItemDecoration);
|
||||||
|
return recyclerView;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user