Merge branch 'feature/slider' into 'master'

Feature/slider



See merge request !18
This commit is contained in:
pengfeizhou 2019-11-20 16:06:08 +08:00
commit c0a3fc073b
17 changed files with 232 additions and 80 deletions

View File

@ -4,7 +4,7 @@ android {
compileSdkVersion 29
buildToolsVersion '29.0.2'
defaultConfig {
applicationId "com.github.penfeizhou.doric"
applicationId "pub.doric.demo"
minSdkVersion 16
targetSdkVersion 29
versionCode 1

View File

@ -19,7 +19,7 @@ import android.graphics.drawable.Drawable;
import androidx.annotation.Nullable;
import android.view.ViewGroup;
import android.text.TextUtils;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
@ -31,7 +31,7 @@ import com.bumptech.glide.request.target.Target;
import pub.doric.DoricContext;
import pub.doric.extension.bridge.DoricPlugin;
import com.github.pengfeizhou.jscore.JSObject;
import com.github.pengfeizhou.jscore.JSONBuilder;
import com.github.pengfeizhou.jscore.JSValue;
/**
@ -41,6 +41,8 @@ import com.github.pengfeizhou.jscore.JSValue;
*/
@DoricPlugin(name = "Image")
public class ImageNode extends ViewNode<ImageView> {
private String loadCallbackId = "";
public ImageNode(DoricContext doricContext) {
super(doricContext);
}
@ -52,22 +54,51 @@ public class ImageNode extends ViewNode<ImageView> {
@Override
protected void blend(ImageView view, String name, JSValue prop) {
if ("imageUrl".equals(name)) {
Glide.with(getContext()).load(prop.asString().value())
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
switch (name) {
case "imageUrl":
Glide.with(getContext()).load(prop.asString().value())
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
if (!TextUtils.isEmpty(loadCallbackId)) {
callJSResponse(loadCallbackId);
}
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
return false;
}
})
.into(view);
} else {
super.blend(view, name, prop);
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
if (!TextUtils.isEmpty(loadCallbackId)) {
callJSResponse(loadCallbackId, new JSONBuilder()
.put("width", resource.getIntrinsicWidth())
.put("height", resource.getIntrinsicHeight())
.toJSONObject());
}
return false;
}
})
.into(view);
break;
case "scaleType":
int scaleType = prop.asNumber().toInt();
switch (scaleType) {
case 1:
view.setScaleType(ImageView.ScaleType.FIT_CENTER);
break;
case 2:
view.setScaleType(ImageView.ScaleType.CENTER_CROP);
break;
default:
view.setScaleType(ImageView.ScaleType.FIT_XY);
break;
}
break;
case "loadCallback":
this.loadCallbackId = prop.asString().value();
break;
default:
super.blend(view, name, prop);
break;
}
}
}

View File

@ -57,6 +57,8 @@ public class TextNode extends ViewNode<TextView> {
case "textAlignment":
view.setGravity(prop.asNumber().toInt() | Gravity.CENTER_VERTICAL);
break;
case "numberOfLines":
break;
default:
super.blend(view, name, prop);
break;

View File

@ -135,12 +135,6 @@ public class HVScrollView extends FrameLayout implements NestedScrollingParent,
*/
private VelocityTracker mVelocityTracker;
/**
* When set to true, the scroll view measure its child to make it fill the currently
* visible area.
*/
private boolean mFillViewport;
/**
* Whether arrow scrolling is animated.
*/
@ -203,9 +197,6 @@ public class HVScrollView extends FrameLayout implements NestedScrollingParent,
final TypedArray a = context.obtainStyledAttributes(
attrs, SCROLLVIEW_STYLEABLE, defStyleAttr, 0);
setFillViewport(a.getBoolean(0, false));
a.recycle();
mParentHelper = new NestedScrollingParentHelper(this);
@ -513,30 +504,6 @@ public class HVScrollView extends FrameLayout implements NestedScrollingParent,
return false;
}
/**
* Indicates whether this ScrollView's content is stretched to fill the viewport.
*
* @return True if the content fills the viewport, false otherwise.
* @attr name android:fillViewport
*/
public boolean isFillViewport() {
return mFillViewport;
}
/**
* Set whether this ScrollView should stretch its content height to fill the viewport or not.
*
* @param fillViewport True to stretch the content's height to the viewport's
* boundaries, false otherwise.
* @attr name android:fillViewport
*/
public void setFillViewport(boolean fillViewport) {
if (fillViewport != mFillViewport) {
mFillViewport = fillViewport;
requestLayout();
}
}
/**
* @return Whether arrow scrolling will animate its transition.
*/
@ -566,10 +533,6 @@ public class HVScrollView extends FrameLayout implements NestedScrollingParent,
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!mFillViewport) {
return;
}
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode == MeasureSpec.UNSPECIFIED && widthMode == MeasureSpec.UNSPECIFIED) {
@ -587,14 +550,17 @@ public class HVScrollView extends FrameLayout implements NestedScrollingParent,
int childWidthMeasureSpec;
int childHeightMeasureSpec;
final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (child.getMeasuredWidth() < width) {
if (lp.width != ViewGroup.LayoutParams.MATCH_PARENT && lp.height != ViewGroup.LayoutParams.MATCH_PARENT) {
return;
}
if (child.getMeasuredWidth() < width && lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
} else {
widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeft() + getPaddingRight(), lp.width);
}
if (child.getMeasuredHeight() < height) {
if (child.getMeasuredHeight() < height && lp.height == ViewGroup.LayoutParams.MATCH_PARENT) {
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
} else {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);

View File

@ -6,4 +6,5 @@ export default [
'src/SliderDemo',
'src/LayoutDemo',
'src/EffectsDemo',
'src/ImageDemo',
]

View File

@ -1,6 +1,6 @@
import { Group, Panel, Text, text, gravity, Color, Stack, LayoutSpec, vlayout, hlayout, scroller, IVLayout, IHLayout, layoutConfig } from "doric";
import { colors } from "./colorutils";
import { colors } from "./utils";
function box(idx = 0) {
@ -361,7 +361,7 @@ class EffectsDemo extends Panel {
it.space = 20
}),
).also(it => {
it.layoutConfig = layoutConfig().wrap()
it.layoutConfig = layoutConfig().atmost()
}).in(rootView)
}
}

64
demo/src/ImageDemo.ts Normal file
View File

@ -0,0 +1,64 @@
import { Group, Panel, List, text, gravity, Color, Stack, LayoutSpec, list, NativeCall, listItem, log, vlayout, Gravity, hlayout, Text, scroller, layoutConfig, image, IView, IVLayout, ScaleType } from "doric";
import { colors, label } from "./utils";
const imageUrl = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574244792703&di=c49ed8cd284c367fa8f00065a85428bd&imgtype=0&src=http%3A%2F%2Fimg3.iqilu.com%2Fdata%2Fattachment%2Fforum%2F201308%2F21%2F201709zikkhkjh7dgfi9f0.jpg'
@Entry
class ImageDemo extends Panel {
build(rootView: Group): void {
scroller(vlayout([
text({
text: "Image Demo",
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30,
textColor: Color.WHITE,
bgColor: colors[1],
textAlignment: gravity().center(),
height: 50,
}),
label('ScaleToFill'),
image({
imageUrl,
width: 300,
height: 300,
border: {
width: 2,
color: Color.GRAY,
},
scaleType: ScaleType.ScaleToFill,
layoutConfig: layoutConfig().exactly(),
loadCallback: (ret) => {
log('loadCallback', ret)
}
}),
label('ScaleAspectFit'),
image({
imageUrl,
width: 300,
height: 300,
border: {
width: 2,
color: Color.GRAY,
},
scaleType: ScaleType.ScaleAspectFit,
layoutConfig: layoutConfig().exactly(),
}),
label('ScaleAspectFill'),
image({
imageUrl,
width: 300,
height: 300,
border: {
width: 2,
color: Color.GRAY,
},
scaleType: ScaleType.ScaleAspectFill,
layoutConfig: layoutConfig().exactly(),
}),
]).apply({
layoutConfig: layoutConfig().atmost().h(LayoutSpec.WRAP_CONTENT),
gravity: gravity().center(),
space: 10,
} as IVLayout)).apply({
layoutConfig: layoutConfig().atmost(),
}).in(rootView)
}
}

View File

@ -1,11 +1,13 @@
import { Group, Panel, List, text, gravity, Color, Stack, LayoutSpec, list, NativeCall, listItem, log, vlayout, Gravity, hlayout, slider, slideItem, image, layoutConfig } from "doric";
import { colors } from "./colorutils";
import { colors } from "./utils";
const imageUrls = [
'http://b.hiphotos.baidu.com/image/pic/item/908fa0ec08fa513db777cf78376d55fbb3fbd9b3.jpg',
'http://f.hiphotos.baidu.com/image/pic/item/0e2442a7d933c8956c0e8eeadb1373f08202002a.jpg',
'http://f.hiphotos.baidu.com/image/pic/item/b151f8198618367aa7f3cc7424738bd4b31ce525.jpg',
'http://b.hiphotos.baidu.com/image/pic/item/0eb30f2442a7d9337119f7dba74bd11372f001e0.jpg',
'http://a.hiphotos.baidu.com/image/h%3D300/sign=b38f3fc35b0fd9f9bf175369152cd42b/9a504fc2d5628535bdaac29e9aef76c6a6ef63c2.jpg',
'http://h.hiphotos.baidu.com/image/pic/item/810a19d8bc3eb1354c94a704ac1ea8d3fd1f4439.jpg',
'http://calonye.com/wp-content/uploads/2015/08/0-wx_fmtgiftpwebpwxfrom5wx_lazy1-9.gif',
'http://hbimg.b0.upaiyun.com/ca29ea125b7f2d580f573e48eb594b1ef509757f34a08-m0hK45_fw658',
'https://misc.aotu.io/ONE-SUNDAY/SteamEngine.png',
@ -31,7 +33,7 @@ class SliderPanel extends Panel {
renderPage: (idx) => {
return slideItem(image({
imageUrl: imageUrls[idx % imageUrls.length],
layoutConfig: layoutConfig().wrap().a(gravity().center()),
layoutConfig: layoutConfig().w(LayoutSpec.WRAP_CONTENT).h(LayoutSpec.WRAP_CONTENT).a(gravity().center()),
})).also(it => {
let start = idx
it.onClick = () => {

View File

@ -1,14 +0,0 @@
import { Color } from "doric";
export const colors = [
"#70a1ff",
"#7bed9f",
"#ff6b81",
"#a4b0be",
"#f0932b",
"#eb4d4b",
"#6ab04c",
"#e056fd",
"#686de0",
"#30336b",
].map(e => Color.parse(e))

36
demo/src/utils.ts Normal file
View File

@ -0,0 +1,36 @@
import { Color, text, Stack, Text } from "doric";
export const colors = [
"#70a1ff",
"#7bed9f",
"#ff6b81",
"#a4b0be",
"#f0932b",
"#eb4d4b",
"#6ab04c",
"#e056fd",
"#686de0",
"#30336b",
].map(e => Color.parse(e))
export function label(str: string) {
return text({
text: str,
textSize: 16,
})
}
export function box(idx = 0) {
return (new Stack).also(it => {
it.width = it.height = 20
it.bgColor = colors[idx || 0]
})
}
export function boxStr(str: string, idx = 0) {
return (new Text).also(it => {
it.width = it.height = 20
it.text = str
it.textColor = Color.WHITE
it.bgColor = colors[idx || 0]
})
}

View File

@ -153,8 +153,8 @@
E2334AF222E9D2060098A085 /* ViewController.m */,
E2334AF422E9D2060098A085 /* Main.storyboard */,
E2334AF722E9D2070098A085 /* Assets.xcassets */,
E2334AF922E9D2070098A085 /* LaunchScreen.storyboard */,
E2334AFC22E9D2070098A085 /* Info.plist */,
E2334AF922E9D2070098A085 /* LaunchScreen.storyboard */,
E2334AFD22E9D2070098A085 /* main.m */,
D751D19E97EF4EDD7588FEBE /* DemoVC.m */,
D751DDEC114E037231257E64 /* DemoVC.h */,

View File

@ -34,6 +34,13 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>LSApplicationCategoryType</key>
<string></string>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>

View File

@ -21,21 +21,57 @@
//
#import "DoricImageNode.h"
#import "Doric.h"
#import <SDWebImage/SDWebImage.h>
@interface DoricImageNode ()
@property(nonatomic, copy) NSString *loadCallbackId;
@end
@implementation DoricImageNode
- (UIImageView *)build {
return [[UIImageView alloc] init];
return [[UIImageView new] also:^(UIImageView *it) {
it.clipsToBounds = YES;
}];
}
- (void)blendView:(UIImageView *)view forPropName:(NSString *)name propValue:(id)prop {
if ([name isEqualToString:@"imageUrl"]) {
if ([@"imageUrl" isEqualToString:name]) {
__weak typeof(self) _self = self;
[view sd_setImageWithURL:[NSURL URLWithString:prop] completed:^(UIImage *_Nullable image, NSError *_Nullable error, SDImageCacheType cacheType, NSURL *_Nullable imageURL) {
__strong typeof(_self) self = _self;
[self requestLayout];
if (error) {
if (self.loadCallbackId.length > 0) {
[self callJSResponse:self.loadCallbackId, nil];
}
} else {
if (self.loadCallbackId.length > 0) {
[self callJSResponse:self.loadCallbackId,
@{@"width": @(image.size.width), @"height": @(image.size.height)},
nil];
}
[self requestLayout];
}
}];
} else if ([@"scaleType" isEqualToString:name]) {
switch ([prop integerValue]) {
case 1: {
self.view.contentMode = UIViewContentModeScaleAspectFit;
break;
}
case 2: {
self.view.contentMode = UIViewContentModeScaleAspectFill;
break;
}
default: {
self.view.contentMode = UIViewContentModeScaleToFill;
break;
}
}
} else if ([@"loadCallback" isEqualToString:name]) {
self.loadCallbackId = prop;
} else {
[super blendView:view forPropName:name propValue:prop];
}

View File

@ -39,6 +39,7 @@ - (instancetype)initWithContext:(DoricContext *)doricContext {
- (void)initWithSuperNode:(DoricSuperNode *)superNode {
[super initWithSuperNode:superNode];
self.reusable = YES;
self.view.clipsToBounds = YES;
}
- (DoricStackView *)build {

View File

@ -152,4 +152,7 @@ - (void)clearSubModel {
- (DoricViewNode *)subNodeWithViewId:(NSString *)viewId {
return nil;
}
- (void)requestLayout {
[self.view setNeedsLayout];
}
@end

View File

@ -23,10 +23,13 @@
#import "DoricTextNode.h"
#import "DoricUtil.h"
#import "DoricGroupNode.h"
#import "Doric.h"
@implementation DoricTextNode
- (UILabel *)build {
return [[UILabel alloc] init];
return [[[UILabel alloc] init] also:^(UILabel *it) {
it.textAlignment = NSTextAlignmentCenter;
}];
}
- (void)blendView:(UILabel *)view forPropName:(NSString *)name propValue:(id)prop {

View File

@ -42,11 +42,25 @@ export class Text extends View implements IText {
textAlignment?: Gravity
}
export enum ScaleType {
ScaleToFill = 0,
ScaleAspectFit,
ScaleAspectFill,
}
export interface IImage extends IView {
imageUrl?: string
scaleType?: ScaleType
loadCallback?: (image: { width: number; height: number } | undefined) => void
}
export class Image extends View implements IImage {
@Property
imageUrl?: string
@Property
scaleType?: ScaleType
@Property
loadCallback?: (image: { width: number; height: number } | undefined) => void
}