Merge branch 'feature/animation' into 'master'

Feature/animation



See merge request !35
This commit is contained in:
pengfeizhou 2019-11-29 15:43:40 +08:00
commit abd9fcdd0a
23 changed files with 652 additions and 54 deletions

View File

@ -15,6 +15,8 @@
*/
package pub.doric;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.content.Context;
import com.github.pengfeizhou.jscore.JSDecoder;
@ -161,6 +163,7 @@ public class DoricContext {
public void reload(String script) {
this.script = script;
this.mRootNode.setId("");
getDriver().createContext(mContextId, script, source);
callEntity(DoricConstant.DORIC_ENTITY_INIT, this.initParams);
}
@ -192,4 +195,15 @@ public class DoricContext {
public IDoricNavBar getDoricNavBar() {
return this.doricNavBar;
}
private AnimatorSet animatorSet;
public void setAnimatorSet(AnimatorSet animatorSet) {
this.animatorSet = animatorSet;
}
public AnimatorSet getAnimatorSet() {
return this.animatorSet;
}
}

View File

@ -20,6 +20,7 @@ import android.text.TextUtils;
import pub.doric.loader.DoricAssetJSLoader;
import pub.doric.loader.DoricHttpJSLoader;
import pub.doric.loader.IDoricJSLoader;
import pub.doric.plugin.AnimatePlugin;
import pub.doric.plugin.NavBarPlugin;
import pub.doric.plugin.NavigatorPlugin;
import pub.doric.plugin.NetworkPlugin;
@ -89,6 +90,7 @@ public class DoricRegistry {
this.registerNativePlugin(NavigatorPlugin.class);
this.registerNativePlugin(NavBarPlugin.class);
this.registerNativePlugin(PopoverPlugin.class);
this.registerNativePlugin(AnimatePlugin.class);
this.registerViewNode(RootNode.class);
this.registerViewNode(TextNode.class);

View File

@ -0,0 +1,86 @@
package pub.doric.plugin;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.text.TextUtils;
import com.github.pengfeizhou.jscore.JSObject;
import com.github.pengfeizhou.jscore.JavaValue;
import java.util.concurrent.Callable;
import pub.doric.DoricContext;
import pub.doric.async.AsyncResult;
import pub.doric.extension.bridge.DoricMethod;
import pub.doric.extension.bridge.DoricPlugin;
import pub.doric.extension.bridge.DoricPromise;
import pub.doric.shader.RootNode;
import pub.doric.shader.ViewNode;
import pub.doric.utils.DoricLog;
import pub.doric.utils.ThreadMode;
/**
* @Description: pub.doric.plugin
* @Author: pengfei.zhou
* @CreateDate: 2019-11-29
*/
@DoricPlugin(name = "animate")
public class AnimatePlugin extends DoricJavaPlugin {
public AnimatePlugin(DoricContext doricContext) {
super(doricContext);
}
@DoricMethod
public void submit(DoricPromise promise) {
promise.resolve();
}
@DoricMethod
public void animateRender(final JSObject jsObject, final DoricPromise promise) {
getDoricContext().getDriver().asyncCall(new Callable<Object>() {
@Override
public Object call() throws Exception {
final long duration = jsObject.getProperty("duration").asNumber().toLong();
AnimatorSet animatorSet = new AnimatorSet();
getDoricContext().setAnimatorSet(animatorSet);
String viewId = jsObject.getProperty("id").asString().value();
RootNode rootNode = getDoricContext().getRootNode();
if (TextUtils.isEmpty(rootNode.getId())) {
rootNode.setId(viewId);
rootNode.blend(jsObject.getProperty("props").asObject());
} else {
ViewNode viewNode = getDoricContext().targetViewNode(viewId);
if (viewNode != null) {
viewNode.blend(jsObject.getProperty("props").asObject());
}
}
getDoricContext().setAnimatorSet(null);
animatorSet.setDuration(duration);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
promise.resolve();
}
});
animatorSet.start();
return null;
}
}, ThreadMode.UI).setCallback(new AsyncResult.Callback<Object>() {
@Override
public void onResult(Object result) {
}
@Override
public void onError(Throwable t) {
t.printStackTrace();
DoricLog.e("Shader.render:error%s", t.getLocalizedMessage());
promise.reject(new JavaValue(t.getLocalizedMessage()));
}
@Override
public void onFinish() {
}
});
}
}

View File

@ -174,7 +174,11 @@ public class ShaderPlugin extends DoricJavaPlugin {
if (clz == DoricPromise.class) {
return doricPromise;
} else {
return jsValue;
try {
return DoricUtils.toJavaObject(clz, jsValue);
} catch (Exception e) {
return jsValue;
}
}
}
}

View File

@ -912,7 +912,7 @@ public class DoricSwipeLayout extends ViewGroup implements NestedScrollingParent
ViewCompat.offsetTopAndBottom(mRefreshView, offset);
mCurrentTargetOffsetTop = mRefreshView.getTop();
if (mRefreshView.getMeasuredHeight() > 0) {
mRefreshView.setProgressRotation((float) mRefreshView.getBottom() / (float) mRefreshView.getMeasuredHeight());
mRefreshView.setProgressRotation((float) mRefreshView.getBottom() / (float) mRefreshView.getMeasuredHeight() * 2);
}
}

View File

@ -15,7 +15,12 @@
*/
package pub.doric.shader;
import android.animation.Animator;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@ -123,19 +128,72 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
protected void blend(T view, String name, JSValue prop) {
switch (name) {
case "width":
setWidth(prop.asNumber().toFloat());
if (isAnimating()) {
addAnimator(ObjectAnimator.ofFloat(
this,
name,
getWidth(),
prop.asNumber().toFloat()));
} else {
setWidth(prop.asNumber().toFloat());
}
break;
case "height":
setHeight(prop.asNumber().toFloat());
if (isAnimating()) {
addAnimator(ObjectAnimator.ofFloat(
this,
name,
getHeight(),
prop.asNumber().toFloat()));
} else {
setHeight(prop.asNumber().toFloat());
}
break;
case "x":
setX(prop.asNumber().toFloat());
if (isAnimating()) {
addAnimator(ObjectAnimator.ofFloat(
this,
name,
getX(),
prop.asNumber().toFloat()));
} else {
setX(prop.asNumber().toFloat());
}
break;
case "y":
setY(prop.asNumber().toFloat());
if (isAnimating()) {
addAnimator(ObjectAnimator.ofFloat(
this,
name,
getY(),
prop.asNumber().toFloat()));
} else {
setY(prop.asNumber().toFloat());
}
break;
case "bgColor":
view.setBackgroundColor(prop.asNumber().toInt());
if (isAnimating()) {
ObjectAnimator animator = ObjectAnimator.ofInt(
this,
name,
getBgColor(),
prop.asNumber().toInt());
animator.setEvaluator(new ArgbEvaluator());
addAnimator(animator);
} else {
setBgColor(prop.asNumber().toInt());
}
break;
case "rotation":
if (isAnimating()) {
addAnimator(ObjectAnimator.ofFloat(
this,
name,
getRotation(),
prop.asNumber().toFloat()));
} else {
setRotation(prop.asNumber().toFloat());
}
break;
case "onClick":
final String functionId = prop.asString().value();
@ -244,30 +302,6 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
return mId;
}
protected void setWidth(float width) {
if (mLayoutParams.width >= 0) {
mLayoutParams.width = DoricUtils.dp2px(width);
}
}
protected void setHeight(float height) {
if (mLayoutParams.height >= 0) {
mLayoutParams.height = DoricUtils.dp2px(height);
}
}
protected void setX(float x) {
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin = DoricUtils.dp2px(x);
}
}
protected void setY(float y) {
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin = DoricUtils.dp2px(y);
}
}
protected void setLayoutConfig(JSObject layoutConfig) {
if (mSuperNode != null) {
mSuperNode.blendSubLayoutConfig(this, layoutConfig);
@ -329,30 +363,95 @@ public abstract class ViewNode<T extends View> extends DoricContextHolder {
}
}
@DoricMethod
public int getWidth() {
return getNodeView().getWidth();
protected boolean isAnimating() {
return getDoricContext().getAnimatorSet() != null;
}
protected void addAnimator(Animator animator) {
if (getDoricContext().getAnimatorSet() == null) {
return;
}
getDoricContext().getAnimatorSet().play(animator);
}
@DoricMethod
public int getHeight() {
return getNodeView().getHeight();
public float getWidth() {
return DoricUtils.px2dp(getNodeView().getWidth());
}
@DoricMethod
public void setRotation(JSValue jsValue) {
float rotation = jsValue.asNumber().toFloat();
while (rotation > 1) {
rotation = rotation - 1;
}
while (rotation < -1) {
rotation = rotation + 1;
}
getNodeView().setRotation(rotation * 360);
public float getHeight() {
return DoricUtils.px2dp(getNodeView().getHeight());
}
@DoricMethod
public void setRotation(float rotation) {
getNodeView().setRotation(rotation * 180);
}
@DoricMethod
public float getRotation() {
return getNodeView().getRotation() / 360;
return getNodeView().getRotation() / 180;
}
@DoricMethod
protected void setWidth(float width) {
if (mLayoutParams.width >= 0) {
mLayoutParams.width = DoricUtils.dp2px(width);
mView.requestLayout();
}
}
@DoricMethod
protected void setHeight(float height) {
if (mLayoutParams.height >= 0) {
mLayoutParams.height = DoricUtils.dp2px(height);
mView.requestLayout();
}
}
@DoricMethod
protected void setX(float x) {
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin = DoricUtils.dp2px(x);
mView.requestLayout();
}
}
@DoricMethod
protected void setY(float y) {
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin = DoricUtils.dp2px(y);
mView.requestLayout();
}
}
@DoricMethod
public float getX() {
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
return DoricUtils.px2dp(((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin);
}
return 0;
}
@DoricMethod
public float getY() {
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
return DoricUtils.px2dp(((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin);
}
return 0;
}
@DoricMethod
public int getBgColor() {
if (mView.getBackground() instanceof ColorDrawable) {
return ((ColorDrawable) mView.getBackground()).getColor();
}
return Color.TRANSPARENT;
}
@DoricMethod
public void setBgColor(int color) {
mView.setBackgroundColor(color);
}
}

View File

@ -14,5 +14,6 @@ export default [
'src/NavbarDemo',
'src/RefreshableDemo',
'src/FlowLayoutDemo',
'src/PopoverDemo'
'src/PopoverDemo',
'src/AnimatorDemo',
]

128
demo/src/AnimatorDemo.ts Normal file
View File

@ -0,0 +1,128 @@
import { animate, Group, Panel, gravity, Color, LayoutSpec, vlayout, scroller, layoutConfig, IVLayout, modal, IText, network, View, stack, IHLayout, hlayout, IView, text } from "doric";
import { title, colors, box } from "./utils";
function thisLabel(str: string) {
return text({
text: str,
width: 100,
height: 50,
bgColor: colors[4],
textSize: 20,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
})
}
@Entry
class AnimatorDemo extends Panel {
build(rootView: Group): void {
const view = box(2)
let idx = 0
vlayout([
title("Animator zDemo"),
vlayout(
[
hlayout([
thisLabel('Reset').apply({
onClick: () => {
animate(this)({
animations: () => {
view.width = view.height = 20
view.x = view.y = 0
view.rotation = 0
view.bgColor = colors[2]
},
duration: 1500,
}).then(() => {
modal(context).toast('Fininshed')
}).catch(e => {
modal(context).toast(`${e}`)
})
}
}),
thisLabel('Move X').apply({
onClick: () => {
animate(this)({
animations: () => {
view.x = view.x || 0
view.x += 100
},
duration: 1000,
})
}
}),
thisLabel('Move Y').apply({
onClick: () => {
animate(this)({
animations: () => {
view.y = view.y || 0
view.y += 100
},
duration: 1000,
})
}
}),
]).apply({ space: 10 } as IHLayout),
hlayout([
thisLabel('Width').apply({
onClick: () => {
animate(this)({
animations: () => {
view.width += 100
},
duration: 1000,
})
}
}),
thisLabel('Height').apply({
onClick: () => {
animate(this)({
animations: () => {
view.height += 100
},
duration: 1000,
})
}
}),
]).apply({ space: 10 } as IHLayout),
hlayout([
thisLabel('BgColor').apply({
onClick: () => {
animate(this)({
animations: () => {
view.bgColor = colors[(idx++) % colors.length]
},
duration: 1000,
});
}
}),
thisLabel('Rotation').apply({
onClick: () => {
animate(this)({
animations: () => {
if (view.rotation) {
view.rotation += 0.5
} else {
view.rotation = 0.5
}
},
duration: 1000,
});
}
}),
]).apply({ space: 10 } as IHLayout),
]
).apply({ space: 10 } as IVLayout),
stack([
view
]).apply({
layoutConfig: layoutConfig().atmost(),
bgColor: colors[1].alpha(0.3 * 255),
}),
]).apply({
layoutConfig: layoutConfig().atmost(),
gravity: gravity().center(),
space: 10,
} as IVLayout).in(rootView)
}
}

View File

@ -17,7 +17,11 @@ function fromDir(startPath, filter) {
fromDir(filename, filter);
}
else if (filename.indexOf(filter) >= 0) {
doMerge(startPath, files[i])
try {
doMerge(startPath, files[i])
} catch (e) {
console.log(e)
}
};
};
};

View File

@ -82,6 +82,7 @@ - (void)initContextWithWidth:(CGFloat)width height:(CGFloat)height {
}
- (void)reload:(NSString *)script {
self.rootNode.viewId = nil;
self.script = script;
[self.driver createContext:self.contextId script:script source:self.source];
[self callEntity:DORIC_ENTITY_INIT, self.initialParams, nil];

View File

@ -41,6 +41,7 @@
#import "DoricFlowLayoutItemNode.h"
#import "DoricFlowLayoutNode.h"
#import "DoricPopoverPlugin.h"
#import "DoricAnimatePlugin.h"
@interface DoricRegistry ()
@ -70,6 +71,7 @@ - (void)innerRegister {
[self registerNativePlugin:DoricNavigatorPlugin.class withName:@"navigator"];
[self registerNativePlugin:DoricNavBarPlugin.class withName:@"navbar"];
[self registerNativePlugin:DoricPopoverPlugin.class withName:@"popover"];
[self registerNativePlugin:DoricAnimatePlugin.class withName:@"animate"];
[self registerViewNode:DoricStackNode.class withName:@"Stack"];
[self registerViewNode:DoricVLayoutNode.class withName:@"VLayout"];

View File

@ -0,0 +1,10 @@
//
// Created by pengfei.zhou on 2019/11/29.
//
#import <Foundation/Foundation.h>
#import "DoricNativePlugin.h"
@interface DoricAnimatePlugin : DoricNativePlugin
@end

View File

@ -0,0 +1,33 @@
//
// Created by pengfei.zhou on 2019/11/29.
//
#import "DoricAnimatePlugin.h"
#import "DoricRootNode.h"
@implementation DoricAnimatePlugin
- (void)submit:(NSDictionary *)args withPromise:(DoricPromise *)promise {
[promise resolve:nil];
}
- (void)animateRender:(NSDictionary *)args withPromise:(DoricPromise *)promise {
NSNumber *duration = args[@"duration"];
dispatch_async(dispatch_get_main_queue(), ^{
NSString *viewId = args[@"id"];
[UIView animateWithDuration:[duration floatValue] / 1000
animations:^{
if (self.doricContext.rootNode.viewId == nil) {
self.doricContext.rootNode.viewId = viewId;
[self.doricContext.rootNode blend:args[@"props"]];
} else {
DoricViewNode *viewNode = [self.doricContext targetViewNode:viewId];
[viewNode blend:args[@"props"]];
}
}
completion:^(BOOL finished) {
[promise resolve:nil];
}];
});
}
@end

View File

@ -23,7 +23,6 @@
#import "DoricShaderPlugin.h"
#import "DoricRootNode.h"
#import "DoricUtil.h"
#import "Doric.h"
#import <objc/runtime.h>

View File

@ -109,7 +109,7 @@ - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.contentOffset.y <= 0) {
[self.swipePullingDelegate setProgressRotation:-scrollView.contentOffset.y / self.headerView.height];
[self.swipePullingDelegate setProgressRotation:-scrollView.contentOffset.y / self.headerView.height * 2];
}
}

View File

@ -123,6 +123,8 @@ - (void)blendView:(UIView *)view forPropName:(NSString *)name propValue:(id)prop
view.y = [(NSNumber *) prop floatValue];
} else if ([name isEqualToString:@"bgColor"]) {
view.backgroundColor = DoricColor(prop);
} else if ([name isEqualToString:@"rotation"]) {
[self setRotation:prop];
} else if ([name isEqualToString:@"layoutConfig"]) {
if (self.superNode && [prop isKindOfClass:[NSDictionary class]]) {
[self.superNode blendSubNode:self layoutConfig:prop];
@ -238,13 +240,13 @@ - (void)setRotation:(NSNumber *)rotation {
if (rotation.floatValue == 0) {
self.view.transform = CGAffineTransformIdentity;
} else {
self.view.transform = CGAffineTransformMakeRotation(M_PI * rotation.floatValue * 2);
self.view.transform = CGAffineTransformMakeRotation(M_PI * rotation.floatValue);
}
}
- (NSNumber *)getRotation {
float radius = atan2f((float) self.view.transform.b, (float) self.view.transform.a);
float degree = (float) (radius / M_PI / 2);
float degree = (float) (radius / M_PI);
return @(degree);
}

View File

@ -0,0 +1,48 @@
/*
* 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 { Panel } from "../ui/panel"
import { takeLet } from "../pattern/candies"
export function animate(panel: Panel) {
return (args: {
animations: () => void,
duration: number,
}) => {
return takeLet(panel.context.animate)(it => {
return it.submit().then(() => {
args.animations()
return takeLet(panel.getRootView())(root => {
if (root.isDirty()) {
const model = root.toModel();
(model as any).duration = args.duration
const ret = it.animateRender(model)
root.clean()
return ret
}
for (let v of panel.allHeadViews()) {
if (v.isDirty()) {
const model = v.toModel()
const ret = it.animateRender(model)
it.clean()
return ret
}
}
throw new Error('Cannot find any animated elements')
})
})
}) as Promise<any>
}
}

View File

@ -19,3 +19,4 @@ export * from './navigator'
export * from './network'
export * from './storage'
export * from './popover'
export * from './animate'

View File

@ -0,0 +1,154 @@
/*
* 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.
*/
export enum RepeatMode {
RESTART,
REVERSE,
}
export class Animation {
duration = 100
startDelay = 0
repeatCount = 1
repeatMode = RepeatMode.RESTART
}
export class AnimationSetBuilder {
currentNode: Node
group: AnimationSet
constructor(group: AnimationSet, anim: Animation) {
this.currentNode = group.getNodeForAnimation(anim)
this.group = group
}
with(animation: Animation) {
const node = this.group.getNodeForAnimation(animation)
this.currentNode.addSibling(node)
return this
}
after(animation: Animation) {
const node = this.group.getNodeForAnimation(animation)
this.currentNode.addParent(node)
return this
}
before(animation: Animation) {
const node = this.group.getNodeForAnimation(animation)
this.currentNode.addChild(node)
return this
}
}
class Node {
children: Set<Node> = new Set
siblings: Set<Node> = new Set
parents: Set<Node> = new Set
animation: Animation
built = false
constructor(anim: Animation) {
this.animation = anim
}
addParent(node: Node) {
if (!this.parents.has(node)) {
this.parents.add(node);
node.addChild(this);
}
}
addSibling(node: Node) {
if (!this.siblings.has(node)) {
this.siblings.add(node);
node.addSibling(this);
}
}
addChild(node: Node) {
if (!this.children.has(node)) {
this.children.add(node)
node.addParent(this)
}
}
}
export class AnimationSet {
nodeMap: Map<Animation, Node> = new Map
nodes: Node[] = []
getNodeForAnimation(anim: Animation) {
let node = this.nodeMap.get(anim)
if (node === undefined) {
node = new Node(anim)
this.nodeMap.set(anim, node)
this.nodes.push(node)
}
return node;
}
play(animation: Animation) {
return new AnimationSetBuilder(this, animation)
}
playTogether(animations: Animation[]) {
if (animations.length == 1) {
this.play(animations[0]);
} else {
for (let i = 0; i < animations.length - 1; i++) {
this.play(animations[i]).with(animations[i + 1])
}
}
}
playSequentially(animations: Animation[]) {
if (animations.length == 1) {
this.play(animations[0]);
} else {
for (let i = 0; i < animations.length - 1; i++) {
this.play(animations[i]).before(animations[i + 1])
}
}
}
findSiblings(node: Node, siblings: Set<Node>) {
if (!siblings.has(node)) {
siblings.add(node)
node.siblings.forEach(e => {
this.findSiblings(e, siblings)
})
}
}
createDependencyGraph() {
this.nodes.forEach(node => {
if (node.built) {
return
}
this.findSiblings(node, node.siblings)
node.siblings.delete(node)
node.siblings.forEach(e => {
e.parents.forEach(p => {
node.addParent(p)
})
})
node.built = true
node.siblings.forEach(s => {
node.parents.forEach(p => {
s.addParent(p)
})
s.built = true
})
})
}
}

View File

@ -14,4 +14,5 @@
* limitations under the License.
*/
export * from './view'
export * from './panel'
export * from './panel'
export * from './animation'

View File

@ -49,7 +49,9 @@ export abstract class Panel {
addHeadView(v: View) {
this.headviews.set(v.viewId, v)
}
allHeadViews() {
return this.headviews.values()
}
removeHeadView(v: View | string) {
if (v instanceof View) {
this.headviews.delete(v.viewId)

View File

@ -61,6 +61,9 @@ export abstract class View implements Modeling, IView {
@Property
bgColor?: Color | GradientColor
@Property
rotation?: number
@Property
corners?: number | { leftTop?: number; rightTop?: number; leftBottom?: number; rightBottom?: number }

View File

@ -62,6 +62,10 @@ export class Color implements Modeling {
}
}
alpha(v: number) {
return new Color((this._value & 0xffffff) | ((v & 0xff) << 24))
}
toModel() {
return this._value
}