Merge branch 'feature/listview' into 'master'

Feature/listview



See merge request !12
This commit is contained in:
pengfeizhou 2019-11-18 17:12:13 +08:00
commit 484d2be892
15 changed files with 328 additions and 22 deletions

View File

@ -19,13 +19,22 @@ import pub.doric.DoricContext;
import pub.doric.async.AsyncResult; import pub.doric.async.AsyncResult;
import pub.doric.extension.bridge.DoricMethod; import pub.doric.extension.bridge.DoricMethod;
import pub.doric.extension.bridge.DoricPlugin; import pub.doric.extension.bridge.DoricPlugin;
import pub.doric.extension.bridge.DoricPromise;
import pub.doric.shader.SuperNode;
import pub.doric.shader.ViewNode;
import pub.doric.utils.DoricLog; import pub.doric.utils.DoricLog;
import pub.doric.utils.DoricMetaInfo;
import pub.doric.utils.DoricUtils;
import pub.doric.utils.ThreadMode; import pub.doric.utils.ThreadMode;
import pub.doric.shader.RootNode; import pub.doric.shader.RootNode;
import com.github.pengfeizhou.jscore.ArchiveException;
import com.github.pengfeizhou.jscore.JSDecoder; import com.github.pengfeizhou.jscore.JSDecoder;
import com.github.pengfeizhou.jscore.JSObject; import com.github.pengfeizhou.jscore.JSObject;
import com.github.pengfeizhou.jscore.JSValue;
import com.github.pengfeizhou.jscore.JavaValue;
import java.lang.reflect.Method;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
/** /**
@ -71,6 +80,90 @@ public class ShaderPlugin extends DoricJavaPlugin {
e.printStackTrace(); e.printStackTrace();
DoricLog.e("Shader.render:error%s", e.getLocalizedMessage()); DoricLog.e("Shader.render:error%s", e.getLocalizedMessage());
} }
}
@DoricMethod
public JavaValue command(JSDecoder jsDecoder, final DoricPromise doricPromise) {
try {
final JSObject jsObject = jsDecoder.decode().asObject();
final JSValue[] viewIds = jsObject.getProperty("viewIds").asArray().toArray();
final String name = jsObject.getProperty("name").asString().value();
final JSValue args = jsObject.getProperty("args");
ViewNode viewNode = null;
for (JSValue value : viewIds) {
if (viewNode == null) {
viewNode = getDoricContext().getRootNode();
} else {
if (value.isString() && viewNode instanceof SuperNode) {
String viewId = value.asString().value();
viewNode = ((SuperNode) viewNode).getSubNodeById(viewId);
}
}
}
if (viewNode == null) {
doricPromise.reject(new JavaValue("Cannot find opposite view"));
} else {
final ViewNode targetViewNode = viewNode;
DoricMetaInfo<ViewNode> pluginInfo = getDoricContext().getDriver().getRegistry()
.acquireViewNodeInfo(viewNode.getType());
final Method method = pluginInfo.getMethod(name);
if (method == null) {
String errMsg = String.format(
"Cannot find plugin method in class:%s,method:%s",
viewNode.getClass(),
name);
doricPromise.reject(new JavaValue(errMsg));
} else {
Callable<JavaValue> callable = new Callable<JavaValue>() {
@Override
public JavaValue call() throws Exception {
Class[] classes = method.getParameterTypes();
Object ret;
if (classes.length == 0) {
ret = method.invoke(targetViewNode);
} else if (classes.length == 1) {
ret = method.invoke(targetViewNode,
createParam(classes[0], doricPromise, args));
} else {
ret = method.invoke(targetViewNode,
createParam(classes[0], doricPromise, args),
createParam(classes[1], doricPromise, args));
}
return DoricUtils.toJavaValue(ret);
}
};
AsyncResult<JavaValue> asyncResult = getDoricContext().getDriver()
.asyncCall(callable, ThreadMode.UI);
asyncResult.setCallback(new AsyncResult.Callback<JavaValue>() {
@Override
public void onResult(JavaValue result) {
doricPromise.resolve(result);
}
@Override
public void onError(Throwable t) {
doricPromise.resolve(new JavaValue(t.getLocalizedMessage()));
}
@Override
public void onFinish() {
}
});
}
}
} catch (ArchiveException e) {
e.printStackTrace();
}
return new JavaValue(true);
}
private Object createParam(Class clz, DoricPromise doricPromise, JSValue jsValue) {
if (clz == DoricPromise.class) {
return doricPromise;
} else {
return jsValue;
}
} }
} }

View File

@ -152,4 +152,14 @@ public abstract class GroupNode<F extends ViewGroup> extends SuperNode<F> {
} }
} }
} }
@Override
public ViewNode getSubNodeById(String id) {
for (ViewNode node : mChildNodes) {
if (id.equals(node.getId())) {
return node;
}
}
return null;
}
} }

View File

@ -40,6 +40,8 @@ public abstract class SuperNode<V extends View> extends ViewNode<V> {
super(doricContext); super(doricContext);
} }
public abstract ViewNode getSubNodeById(String id);
protected ViewGroup.LayoutParams generateDefaultLayoutParams() { protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new ViewGroup.LayoutParams(0, 0); return new ViewGroup.LayoutParams(0, 0);
} }

View File

@ -24,6 +24,7 @@ import android.widget.LinearLayout;
import pub.doric.DoricContext; import pub.doric.DoricContext;
import pub.doric.DoricRegistry; import pub.doric.DoricRegistry;
import pub.doric.async.AsyncResult; import pub.doric.async.AsyncResult;
import pub.doric.extension.bridge.DoricMethod;
import pub.doric.utils.DoricContextHolder; import pub.doric.utils.DoricContextHolder;
import pub.doric.utils.DoricConstant; import pub.doric.utils.DoricConstant;
import pub.doric.utils.DoricMetaInfo; import pub.doric.utils.DoricMetaInfo;
@ -243,4 +244,14 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
mSuperNode.blendSubLayoutConfig(this, layoutConfig); mSuperNode.blendSubLayoutConfig(this, layoutConfig);
} }
} }
@DoricMethod
public int getWidth() {
return mView.getWidth();
}
@DoricMethod
public int getHeight() {
return mView.getHeight();
}
} }

View File

@ -15,6 +15,8 @@
*/ */
package pub.doric.shader.list; package pub.doric.shader.list;
import android.view.View;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -91,4 +93,23 @@ public class ListNode extends SuperNode<RecyclerView> {
protected void blendSubLayoutConfig(ViewNode viewNode, JSObject jsObject) { protected void blendSubLayoutConfig(ViewNode viewNode, JSObject jsObject) {
super.blendSubLayoutConfig(viewNode, jsObject); super.blendSubLayoutConfig(viewNode, jsObject);
} }
@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;
}
ListAdapter.DoricViewHolder viewHolder = (ListAdapter.DoricViewHolder) mView.getChildViewHolder(view);
if (id.equals(viewHolder.listItemNode.getId())) {
return viewHolder.listItemNode;
}
}
return null;
}
} }

View File

@ -73,6 +73,13 @@ class ListPanel extends Panel {
it.onClick = () => { it.onClick = () => {
log(`Click item at ${idx}`) log(`Click item at ${idx}`)
it.height += 10 it.height += 10
it.nativeChannel(context, "getWidth")().then(
resolve => {
log(`resolve,${resolve}`)
},
reject => {
log(`reject,${reject}`)
})
} }
}) })
}, },

View File

@ -24,7 +24,6 @@
#import "DoricRegistry.h" #import "DoricRegistry.h"
#import "DoricContextManager.h" #import "DoricContextManager.h"
#import "DoricNativePlugin.h" #import "DoricNativePlugin.h"
#import "DoricPromise.h"
#import "DoricUtil.h" #import "DoricUtil.h"
#import <JavaScriptCore/JavaScriptCore.h> #import <JavaScriptCore/JavaScriptCore.h>
@ -42,30 +41,44 @@ - (id)callNativeWithContextId:(NSString *)contextId module:(NSString *)module me
nativePlugin = [(DoricNativePlugin *) [pluginClass alloc] initWithContext:context]; nativePlugin = [(DoricNativePlugin *) [pluginClass alloc] initWithContext:context];
context.pluginInstanceMap[module] = nativePlugin; context.pluginInstanceMap[module] = nativePlugin;
} }
return [self findClass:pluginClass target:nativePlugin context:context method:method callbackId:callbackId argument:argument];
}
- (id)createParamWithMethodName:(NSString *)method context:(DoricContext *)context callbackId:(NSString *)callbackId argument:(id)argument {
if ([method isEqualToString:@"withPromise"]) {
return [[DoricPromise alloc] initWithContext:context callbackId:callbackId];
}
return argument;
}
- (id)findClass:(Class)clz target:(id)target context:(DoricContext *)context method:(NSString *)name callbackId:(NSString *)callbackId argument:(id)argument {
unsigned int count; unsigned int count;
id ret = nil; id ret = nil;
Method *methods = class_copyMethodList(pluginClass, &count); Method *methods = class_copyMethodList(clz, &count);
BOOL isFound = NO;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(methods[i])) encoding:NSUTF8StringEncoding]; NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(methods[i])) encoding:NSUTF8StringEncoding];
NSArray *array = [methodName componentsSeparatedByString:@":"]; NSArray *array = [methodName componentsSeparatedByString:@":"];
if (array && [array count] > 0) { if (array && [array count] > 0) {
if ([array[0] isEqualToString:method]) { if ([array[0] isEqualToString:name]) {
isFound = YES;
SEL selector = NSSelectorFromString(methodName); SEL selector = NSSelectorFromString(methodName);
NSMethodSignature *methodSignature = [nativePlugin methodSignatureForSelector:selector]; NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
if (methodSignature) { if (methodSignature) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
invocation.selector = selector; invocation.selector = selector;
invocation.target = nativePlugin; invocation.target = target;
__weak __typeof__(self) _self = self; __weak __typeof__(self) _self = self;
void (^block)(void) = ^() { dispatch_block_t block = ^() {
__strong __typeof__(_self) self = _self; __strong __typeof__(_self) self = _self;
@try { @try {
for (int i = 2; i < methodSignature.numberOfArguments; i++) { for (NSUInteger idx = 2; idx < methodSignature.numberOfArguments; idx++) {
if (i - 2 > [array count]) { if (idx - 2 > [array count]) {
break; break;
} }
id args = [self createParamWithMethodName:array[i - 2] context:context callbackId:callbackId argument:argument]; id args = [self createParamWithMethodName:array[idx - 2] context:context callbackId:callbackId argument:argument];
[invocation setArgument:&args atIndex:i]; [invocation setArgument:&args atIndex:idx];
} }
[invocation invoke]; [invocation invoke];
} @catch (NSException *exception) { } @catch (NSException *exception) {
@ -95,14 +108,13 @@ - (id)callNativeWithContextId:(NSString *)contextId module:(NSString *)module me
if (methods) { if (methods) {
free(methods); free(methods);
} }
return ret; if (!isFound) {
} Class superclass = class_getSuperclass(clz);
if (superclass && superclass != [NSObject class]) {
- (id)createParamWithMethodName:(NSString *)method context:(DoricContext *)context callbackId:(NSString *)callbackId argument:(id)argument { return [self findClass:superclass target:target context:context method:name callbackId:callbackId argument:argument];
if ([method isEqualToString:@"withPromise"]) { }
return [[DoricPromise alloc] initWithContext:context callbackId:callbackId];
} }
return argument; return ret;
}
}
@end @end

View File

@ -22,10 +22,15 @@
#import "DoricShaderPlugin.h" #import "DoricShaderPlugin.h"
#import "DoricRootNode.h" #import "DoricRootNode.h"
#import "DoricUtil.h"
#import <JavaScriptCore/JavaScriptCore.h>
#import <objc/runtime.h>
@implementation DoricShaderPlugin @implementation DoricShaderPlugin
- (void)render:(NSDictionary *)argument withPromise:(DoricPromise *)promise { - (void)render:(NSDictionary *)argument {
__weak typeof(self) _self = self; __weak typeof(self) _self = self;
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(_self) self = _self; __strong typeof(_self) self = _self;
@ -33,4 +38,92 @@ - (void)render:(NSDictionary *)argument withPromise:(DoricPromise *)promise {
}); });
} }
- (id)command:(NSDictionary *)argument withPromise:(DoricPromise *)promise {
NSArray *viewIds = argument[@"viewIds"];
id args = argument[@"args"];
NSString *name = argument[@"name"];
DoricViewNode *viewNode = nil;
for (NSString *viewId in viewIds) {
if (!viewNode) {
viewNode = self.doricContext.rootNode;
} else {
if ([viewNode isKindOfClass:[DoricSuperNode class]]) {
viewNode = [((DoricSuperNode *) viewNode) subNodeWithViewId:viewId];
}
}
}
if (!viewNode) {
[promise reject:@"Cannot find opposite view"];
return nil;
} else {
return [self findClass:[viewNode class] target:viewNode method:name promise:promise argument:args];
}
}
- (id)createParamWithMethodName:(NSString *)method promise:(DoricPromise *)promise argument:(id)argument {
if ([method isEqualToString:@"withPromise"]) {
return promise;
}
return argument;
}
- (id)findClass:(Class)clz target:(id)target method:(NSString *)name promise:(DoricPromise *)promise argument:(id)argument {
unsigned int count;
id ret = nil;
Method *methods = class_copyMethodList(clz, &count);
BOOL isFound = NO;
for (int i = 0; i < count; i++) {
NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(methods[i])) encoding:NSUTF8StringEncoding];
NSArray *array = [methodName componentsSeparatedByString:@":"];
if (array && [array count] > 0) {
if ([array[0] isEqualToString:name]) {
isFound = YES;
SEL selector = NSSelectorFromString(methodName);
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
if (methodSignature) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
invocation.selector = selector;
invocation.target = target;
__weak __typeof__(self) _self = self;
dispatch_block_t block = ^() {
__strong __typeof__(_self) self = _self;
@try {
for (NSUInteger idx = 2; idx < methodSignature.numberOfArguments; idx++) {
if (idx - 2 > [array count]) {
break;
}
id param = [self createParamWithMethodName:array[idx - 2] promise:promise argument:argument];
[invocation setArgument:&param atIndex:idx];
}
[invocation invoke];
} @catch (NSException *exception) {
DoricLog(@"CallNative Error:%@", exception.reason);
}
};
dispatch_async(dispatch_get_main_queue(), ^{
void *retValue;
block();
[invocation getReturnValue:&retValue];
id returnValue = (__bridge id) retValue;
[promise resolve:returnValue];
});
return ret;
}
break;
}
}
}
if (methods) {
free(methods);
}
if (!isFound) {
Class superclass = class_getSuperclass(clz);
if (superclass && superclass != [NSObject class]) {
return [self findClass:superclass target:target method:name promise:promise argument:argument];
}
}
return ret;
}
@end @end

View File

@ -159,4 +159,14 @@ - (void)blendSubNode:(NSDictionary *)subModel {
} }
}]; }];
} }
- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId {
for (DoricViewNode *viewNode in self.childNodes) {
if ([viewId isEqualToString:viewNode.viewId]) {
return viewNode;
}
}
return nil;
}
@end @end

View File

@ -150,4 +150,21 @@ - (void)callItem:(NSUInteger)position height:(CGFloat)height {
[self.view reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; [self.view reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}]; }];
} }
- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId {
__block DoricViewNode *ret = nil;
dispatch_sync(dispatch_get_main_queue(), ^{
for (UITableViewCell *tableViewCell in self.view.visibleCells) {
if ([tableViewCell isKindOfClass:[DoricTableViewCell class]]) {
DoricListItemNode *node = ((DoricTableViewCell *) tableViewCell).doricListItemNode;
if ([viewId isEqualToString:node.viewId]) {
ret = node;
break;
}
}
}
});
return ret;
}
@end @end

View File

@ -32,4 +32,6 @@
- (void)setSubModel:(NSDictionary *)model in:(NSString *)viewId; - (void)setSubModel:(NSDictionary *)model in:(NSString *)viewId;
- (void)clearSubModel; - (void)clearSubModel;
- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId;
@end @end

View File

@ -122,4 +122,7 @@ - (void)clearSubModel {
[self.subNodes removeAllObjects]; [self.subNodes removeAllObjects];
} }
- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId {
return nil;
}
@end @end

View File

@ -220,4 +220,12 @@ - (void)requestLayout {
[self.superNode requestLayout]; [self.superNode requestLayout];
} }
- (NSNumber *)getWidth {
return @(self.view.width);
}
- (NSNumber *)getHeight {
return @(self.view.height);
}
@end @end

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import { LayoutConfig, Group, Property, IView } from "./view"; import { Group, Property, IView } from "./view";
import { Gravity } from "../util/gravity"; import { Gravity } from "../util/gravity";
export interface IStack extends IView { export interface IStack extends IView {
@ -22,8 +22,6 @@ export interface IStack extends IView {
export class Stack extends Group implements IStack { export class Stack extends Group implements IStack {
} }
export class Root extends Stack { export class Root extends Stack {
} }

View File

@ -255,6 +255,24 @@ export abstract class View implements Modeling, IView {
in(group: Group) { in(group: Group) {
group.addChild(this) group.addChild(this)
} }
nativeChannel(context: any, name: string) {
let thisView: View | undefined = this
return function (...args: any) {
const func = context.shader.command
const viewIds = []
while (thisView != undefined) {
viewIds.push(thisView.viewId)
thisView = thisView.superview
}
const params = {
viewIds: viewIds.reverse(),
name,
args,
}
return Reflect.apply(func, undefined, [params]) as Promise<any>
}
}
} }
export abstract class Superview extends View { export abstract class Superview extends View {
@ -315,6 +333,7 @@ export abstract class Group extends Superview {
} }
addChild(view: View) { addChild(view: View) {
view.superview = this
this.children.push(view) this.children.push(view)
} }
} }