6 个版本
0.1.5 | 2022年7月18日 |
---|---|
0.1.4 | 2022年7月14日 |
#1231 在 GUI
89KB
2K SLoC
floui-rs
Rust 对 floui 的绑定,发音为 "flowy",是一个受 SwiftUI 启发的概念验证 C++17 库,它包装了原生 iOS 和 Android 控件/小部件,并集成到每个平台的事实上的构建环境中(XCode 和 Android Studio)。
当前可用的控件
- 文本
- 文本字段
- 按钮
- VStack(iOS 上的垂直 UIStackView 和 Android 上的 LinearLayout)
- HStack(iOS 上的水平 UIStackView 和 Android 上的 LinearLayout)
- 空间
- 切换/复选框(tvOS 不支持它)
- 滑动条(tvOS 不支持它)
- 图片视图
- 网页视图
- 滚动视图
用法
您可以查看 floui-rs-template,该模板结构化以能够从命令行构建 ios 或 android。
如果您只想为 iOS 构建,并且仅使用 Rust(不使用 Objective-C),请查看此处的示例 此处。
否则,如果您想手动构建
将库构建为静态库
# Cargo.toml
[lib]
crate-type = ["static-lib"]
[dependencies]
floui = "0.1"
针对您可能需要的 android 和 ios 架构进行构建。
Rust
use floui::{enums::*, prelude::*, widgets::*};
use std::cell::RefCell;
use std::rc::Rc;
fn mygui(vc: &ViewController) -> MainView {
let count = Rc::from(RefCell::from(0));
MainView::new(
&vc,
&[
&Button::new("Increment").foreground(Color::Blue).action({
let count = count.clone();
move |_| {
log("Increment clicked");
let mut c = count.borrow_mut();
*c += 1;
let t: Text = from_id("mytext").unwrap();
t.text(&format!("{}", c));
}
}),
&Text::new("0").id("mytext").center().bold(),
&Button::new("Decrement")
.foreground(Color::Red)
.action(move |_| {
log("Decrement clicked");
let mut c = count.borrow_mut();
*c -= 1;
let t: Text = from_id("mytext").unwrap();
t.text(&format!("{}", c));
}),
],
)
}
use std::os::raw::c_void;
#[no_mangle]
extern "C" fn floui_handle_events(arg1: *mut c_void) {
unsafe { ViewController::handle_events(arg1); }
}
#[no_mangle]
extern "C" fn floui_main(arg1: *mut c_void, arg2: *mut c_void, arg3: *mut c_void) -> *mut c_void {
let vc = unsafe { ViewController::new(arg1, arg2, arg3) };
mygui(&vc).underlying() as _
}
关于某些用法的说明
-
Android 上的滑动条占满 LinearLayout 的全部宽度,因此如果代码也与 iOS 共享,则需要考虑这一点。
-
添加图像必须在项目的资源文件中完成。
- 在 Android Studio 中:资源管理器,导入可绘制资源。这将文件添加到 res/drawable。可以直接访问 ImageView::load("MyImage.jpg")。
- 在 XCode 中:您可以简单地拖动图像到 Assets.xcassets,然后可以直接访问 ImageView::load("MyImage.jpg")。
-
使用 WebView 小部件
- 在 iOS 上
- 需要在“通用”>“框架”、“库和嵌入内容”下添加 WebKit.framework。
- 需要在 Cargo.toml 中启用
ios-webview
标志。 - 本地文件可以使用 WebView::load_url() 加载,但需要在前面加上
file:///
,文件应添加到您的 xcode 项目中。
- 在 Android 上
- 要加载本地文件,请在前面加上
file:///
和文件的路径,该路径应添加到 assets 文件夹中(文件 > 新建 > 文件夹 > Assets 文件夹)。然后可以使用 WebView::load_url() 加载。 - 要加载 HTTP 请求,您需要在 AndroidManifest.xml 中启用互联网权限:
<uses-permission android:name="android.permission.INTERNET" />
- 要加载本地文件,请在前面加上
- 在 iOS 上
创建新小部件
包裹平台小部件不需要使用 C++,它需要该小部件实现 WidgetExt,并且可以检索到底层(Android 的 JNI jobject 指针,iOS 的 UIView)。
特定于目标的结构
iOS
- 添加所需的 ios rustup 目标。
- 安装 cargo-lipo。
- 将构建的库添加到您的 xcode 项目中(在 Build Phases > Link Binary with Libraries 下)。
- 修改库搜索路径以找到库(在 Build Settings > Library Search Paths 下)。
- 修改您的 ViewController.m 文件
#import "ViewController.h"
extern void *floui_main(void *, void *, void *);
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
floui_main((void *)CFBridgingRetain(self), nil, nil);
}
@end
Android
- ANDROID_SDK_ROOT 应设置为您的 Android SDK 目录。
- ANDROID_NDK_ROOT 应设置为您的 Android NDK 目录。
- 添加所需的 android rustup 目标。
- 创建一个 Android Studio 本地 C++ 项目,最后一步选择工具链 C++ 17。
- 修改您的 CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
project(myapplication)
find_library(log-lib log)
add_library(myapplication SHARED native-lib.cpp)
add_library(rust-lib STATIC IMPORTED)
if (ANDROID_ABI STREQUAL x86)
set(RUST_ARCH i686-linux-android)
elseif (ANDROID_ABI STREQUAL armeabi-v7a)
set(RUST_ARCH armv7-linux-androideabi)
elseif (ANDROID_ABI STREQUAL arm64-v8a)
set(RUST_ARCH aarch64-linux-android)
elseif (ANDROID_ABI STREQUAL x86_64)
set(RUST_ARCH x86_64-linux-android)
else ()
message(FATAL "Unknown architecture")
endif ()
set_property(TARGET rust-lib PROPERTY IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_LIST_DIR}/app/target/${RUST_ARCH}/debug/libapp.a)
set_property(TARGET rust-lib PROPERTY IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_LIST_DIR}/app/target/${RUST_ARCH}/release/libapp.a)
target_link_libraries(myapplication android rust-lib ${log-lib})
- 修改您的 C++ 文件,使其仅调用 Rust 库。
#include <jni.h>
#include <string>
extern "C" void *floui_main(void *, void *, void *);
extern "C" void floui_handle_events(void *);
extern "C" JNIEXPORT jobject JNICALL
Java_com_example_myapplication_MainActivity_mainView(JNIEnv* env, jobject main_activity, jobject view) {
return (jobject) floui_main(env, main_activity, view);
}
extern "C" JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_handleEvent(JNIEnv *env, jobject thiz, jobject view) {
floui_handle_events(view);
}
- 修改您的 MainActivity.java,使其看起来像
package com.example.myapplication;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import android.os.Bundle;
import android.view.View;
import com.google.android.material.slider.Slider;
public class MainActivity extends AppCompatActivity implements View.OnClickListener, Slider.OnChangeListener {
static {
System.loadLibrary("myapplication");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ConstraintLayout layout = new ConstraintLayout(this);
setContentView(layout);
mainView(layout);
}
public native View mainView(View view);
public native void handleEvent(View view);
@Override
public void onClick(View view) {
handleEvent(view);
}
@Override
public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
handleEvent(slider);
}
}
依赖项
~190KB