add slider

This commit is contained in:
pengfei.zhou 2019-11-19 14:56:25 +08:00
parent c8353a48d2
commit 353ff356a5
13 changed files with 482 additions and 12 deletions

View File

@ -28,6 +28,8 @@ import pub.doric.shader.StackNode;
import pub.doric.shader.TextNode; import pub.doric.shader.TextNode;
import pub.doric.shader.VLayoutNode; import pub.doric.shader.VLayoutNode;
import pub.doric.shader.ViewNode; import pub.doric.shader.ViewNode;
import pub.doric.shader.slider.SlideItemNode;
import pub.doric.shader.slider.SliderNode;
import pub.doric.utils.DoricMetaInfo; import pub.doric.utils.DoricMetaInfo;
import pub.doric.plugin.DoricJavaPlugin; import pub.doric.plugin.DoricJavaPlugin;
import pub.doric.plugin.ModalPlugin; import pub.doric.plugin.ModalPlugin;
@ -72,6 +74,8 @@ public class DoricRegistry {
this.registerViewNode(ListNode.class); this.registerViewNode(ListNode.class);
this.registerViewNode(ListItemNode.class); this.registerViewNode(ListItemNode.class);
this.registerViewNode(ScrollerNode.class); this.registerViewNode(ScrollerNode.class);
this.registerViewNode(SliderNode.class);
this.registerViewNode(SlideItemNode.class);
initRegistry(this); initRegistry(this);
} }

View File

@ -37,7 +37,7 @@ import pub.doric.shader.ViewNode;
* @Author: pengfei.zhou * @Author: pengfei.zhou
* @CreateDate: 2019-11-12 * @CreateDate: 2019-11-12
*/ */
public class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolder> { class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolder> {
private final ListNode listNode; private final ListNode listNode;
String renderItemFuncId; String renderItemFuncId;

View File

@ -81,7 +81,7 @@ public class ListNode extends SuperNode<RecyclerView> {
clearSubModel(); clearSubModel();
break; break;
case "batchCount": case "batchCount":
this.listAdapter.batchCount = 15; this.listAdapter.batchCount = prop.asNumber().toInt();
break; break;
default: default:
super.blend(view, name, prop); super.blend(view, name, prop);

View File

@ -0,0 +1,136 @@
/*
* 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.slider;
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 SlideAdapter extends RecyclerView.Adapter<SlideAdapter.DoricViewHolder> {
private final SliderNode sliderNode;
int itemCount = 0;
int batchCount = 3;
SparseArray<String> itemValues = new SparseArray<>();
SlideAdapter(SliderNode sliderNode) {
this.sliderNode = sliderNode;
}
@NonNull
@Override
public DoricViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
SlideItemNode node = (SlideItemNode) ViewNode.create(sliderNode.getDoricContext(), "SlideItem");
node.init(sliderNode);
return new DoricViewHolder(node, node.getDoricLayer());
}
@Override
public void onBindViewHolder(@NonNull DoricViewHolder holder, int position) {
JSValue jsValue = getItemModel(position);
if (jsValue.isObject()) {
JSObject jsObject = jsValue.asObject();
holder.slideItemNode.setId(jsObject.getProperty("id").asString().value());
holder.slideItemNode.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 = sliderNode.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);
sliderNode.setSubModel(itemId, itemModel);
}
return sliderNode.getSubModel(itemValues.get(position));
}
} catch (Exception e) {
e.printStackTrace();
}
return new JSNull();
} else {
JSObject childModel = sliderNode.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 {
SlideItemNode slideItemNode;
DoricViewHolder(SlideItemNode node, @NonNull View itemView) {
super(itemView);
slideItemNode = node;
}
}
}

View File

@ -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.slider;
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 = "SlideItem")
public class SlideItemNode extends StackNode {
public String identifier = "";
public SlideItemNode(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);
getDoricLayer().getLayoutParams().width = getLayoutParams().width;
getDoricLayer().getLayoutParams().height = getLayoutParams().height;
}
}

View File

@ -0,0 +1,115 @@
/*
* 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.slider;
import android.view.View;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.PagerSnapHelper;
import androidx.recyclerview.widget.RecyclerView;
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;
/**
* @Description: pub.doric.shader
* @Author: pengfei.zhou
* @CreateDate: 2019-11-19
*/
@DoricPlugin(name = "Slider")
public class SliderNode extends SuperNode<RecyclerView> {
private final SlideAdapter slideAdapter;
public SliderNode(DoricContext doricContext) {
super(doricContext);
this.slideAdapter = new SlideAdapter(this);
}
@Override
protected RecyclerView build() {
RecyclerView recyclerView = new RecyclerView(getContext());
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
PagerSnapHelper mPagerSnapHelper = new PagerSnapHelper();
mPagerSnapHelper.attachToRecyclerView(recyclerView);
recyclerView.setAdapter(this.slideAdapter);
return recyclerView;
}
@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;
}
SlideAdapter.DoricViewHolder viewHolder = (SlideAdapter.DoricViewHolder) mView.getChildViewHolder(view);
if (id.equals(viewHolder.slideItemNode.getId())) {
return viewHolder.slideItemNode;
}
}
return null;
}
@Override
protected void blendSubNode(JSObject subProperties) {
slideAdapter.blendSubNode(subProperties);
}
@Override
public void blend(JSObject jsObject) {
super.blend(jsObject);
if (mView != null) {
mView.post(new Runnable() {
@Override
public void run() {
slideAdapter.notifyDataSetChanged();
}
});
}
}
@Override
protected void blend(RecyclerView view, String name, JSValue prop) {
switch (name) {
case "itemCount":
this.slideAdapter.itemCount = prop.asNumber().toInt();
break;
case "renderItem":
// If reset renderItem,should reset native cache.
this.slideAdapter.itemValues.clear();
clearSubModel();
break;
case "batchCount":
this.slideAdapter.batchCount = prop.asNumber().toInt();
break;
default:
super.blend(view, name, prop);
break;
}
}
}

55
demo/src/SliderDemo.ts Normal file
View File

@ -0,0 +1,55 @@
import { Group, Panel, List, text, gravity, Color, Stack, LayoutSpec, list, NativeCall, listItem, log, vlayout, Gravity, hlayout, slider, slideItem } from "doric";
const colors = [
"#f0932b",
"#eb4d4b",
"#6ab04c",
"#e056fd",
"#686de0",
"#30336b",
]
@Entry
class SliderPanel extends Panel {
build(rootView: Group): void {
rootView.addChild(vlayout([
text({
text: "SliderDemo",
layoutConfig: {
widthSpec: LayoutSpec.AT_MOST,
heightSpec: LayoutSpec.EXACTLY,
},
textSize: 30,
textColor: Color.parse("#535c68"),
bgColor: Color.parse("#dff9fb"),
textAlignment: gravity().center(),
height: 50,
}),
slider({
itemCount: 3,
renderPage: (idx) => {
return slideItem(text({
layoutConfig: {
widthSpec: LayoutSpec.WRAP_CONTENT,
heightSpec: LayoutSpec.EXACTLY,
alignment: gravity().center(),
},
text: `Cell At Line ${idx}`,
textAlignment: gravity().center(),
textColor: Color.parse("#ffffff"),
textSize: 20,
height: 50,
bgColor: Color.parse('#00ff00'),
}))
},
layoutConfig: {
widthSpec: LayoutSpec.AT_MOST,
heightSpec: LayoutSpec.AT_MOST,
},
}),
]).also(it => {
it.layoutConfig = {
widthSpec: LayoutSpec.AT_MOST,
heightSpec: LayoutSpec.AT_MOST,
}
}))
}
}

View File

@ -1,7 +1,18 @@
// /*
// Created by pengfei.zhou on 2019/11/19. * 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.
*/
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "DoricSuperNode.h" #import "DoricSuperNode.h"

View File

@ -1,7 +1,18 @@
// /*
// Created by pengfei.zhou on 2019/11/19. * 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.
*/
#import "DoricScrollerNode.h" #import "DoricScrollerNode.h"
#import "DoricExtensions.h" #import "DoricExtensions.h"

View File

@ -15,7 +15,8 @@
*/ */
export * from "./src/ui/view" export * from "./src/ui/view"
export * from "./src/ui/layout" export * from "./src/ui/layout"
export * from "./src/ui/listview" export * from "./src/ui/list"
export * from "./src/ui/slider"
export * from "./src/ui/scroller" export * from "./src/ui/scroller"
export * from "./src/ui/widgets" export * from "./src/ui/widgets"
export * from "./src/ui/panel" export * from "./src/ui/panel"

View File

@ -16,7 +16,8 @@
import { View, LayoutSpec } from './view' import { View, LayoutSpec } from './view'
import { Stack, HLayout, VLayout } from './layout' import { Stack, HLayout, VLayout } from './layout'
import { IText, IImage, Text, Image } from './widgets' import { IText, IImage, Text, Image } from './widgets'
import { IList, List } from './listview' import { IList, List } from './list'
import { ISlider, Slider } from './slider'
export function text(config: IText) { export function text(config: IText) {
const ret = new Text const ret = new Text
@ -84,4 +85,12 @@ export function list(config: IList) {
Reflect.set(ret, key, Reflect.get(config, key, config), ret) Reflect.set(ret, key, Reflect.get(config, key, config), ret)
} }
return ret return ret
}
export function slider(config: ISlider) {
const ret = new Slider
for (let key in config) {
Reflect.set(ret, key, Reflect.get(config, key, config), ret)
}
return ret
} }

View File

@ -0,0 +1,72 @@
import { Superview, View, LayoutSpec, Property, IView } from "./view";
import { Stack } from "./layout";
export function slideItem(item: View) {
return (new SlideItem).also((it) => {
it.layoutConfig = {
widthSpec: LayoutSpec.WRAP_CONTENT,
heightSpec: LayoutSpec.WRAP_CONTENT,
}
it.addChild(item)
})
}
export class SlideItem extends Stack {
/**
* Set to reuse native view
*/
@Property
identifier?: string
}
export interface ISlider extends IView {
renderPage: (index: number) => SlideItem
itemCount: number
batchCount?: number
}
export class Slider extends Superview implements ISlider {
private cachedViews: Map<string, SlideItem> = new Map
private ignoreDirtyCallOnce = false
allSubviews() {
return this.cachedViews.values()
}
@Property
itemCount = 0
@Property
renderPage!: (index: number) => SlideItem
@Property
batchCount = 3
private getItem(itemIdx: number) {
let view = this.cachedViews.get(`${itemIdx}`)
if (view === undefined) {
view = this.renderPage(itemIdx)
view.superview = this
this.cachedViews.set(`${itemIdx}`, view)
}
return view
}
isDirty() {
if (this.ignoreDirtyCallOnce) {
this.ignoreDirtyCallOnce = false
//Ignore the dirty call once.
return false
}
return super.isDirty()
}
private renderBunchedItems(start: number, length: number) {
this.ignoreDirtyCallOnce = true;
return new Array(Math.min(length, this.itemCount - start)).fill(0).map((_, idx) => {
const slideItem = this.getItem(start + idx)
return slideItem.toModel()
})
}
}