DeX: Dimensions.getモンキーパッチ + transform scaleで低密度ディスプレイ対応
- Dimensions.get('window')を1/1.3に縮小パッチ
- useWindowDimensions()も自動的に小さい値を返す
- DensityScaleWrapperで1.3倍transform scaleで拡大
- コンポーネントは872x531dpとしてレイアウト→実画面1133x690に拡大
This commit is contained in:
46
App.tsx
46
App.tsx
@@ -1,5 +1,10 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { Linking, Platform, UIManager } from "react-native";
|
||||
import {
|
||||
IS_LOW_DENSITY,
|
||||
DEX_SCALE,
|
||||
useRealWindowDimensions,
|
||||
} from "./utils/dexDimensionOverride";
|
||||
import { Linking, Platform, UIManager, View } from "react-native";
|
||||
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
||||
import "./utils/disableFontScaling"; // グローバルなフォントスケーリング無効化
|
||||
import { AppContainer } from "./Apps";
|
||||
@@ -137,13 +142,42 @@ export default function App() {
|
||||
<DeviceOrientationChangeProvider>
|
||||
<SafeAreaProvider>
|
||||
<StatusbarDetect />
|
||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||
<ProviderTree>
|
||||
<AppContainer />
|
||||
</ProviderTree>
|
||||
</GestureHandlerRootView>
|
||||
<DensityScaleWrapper>
|
||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||
<ProviderTree>
|
||||
<AppContainer />
|
||||
</ProviderTree>
|
||||
</GestureHandlerRootView>
|
||||
</DensityScaleWrapper>
|
||||
</SafeAreaProvider>
|
||||
</DeviceOrientationChangeProvider>
|
||||
</AppThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 低密度ディスプレイ(DeX等)で全体を transform scale で拡大。
|
||||
* Dimensions.get をパッチ済みなので、コンポーネントは内部サイズに合わせてレイアウトする。
|
||||
*/
|
||||
function DensityScaleWrapper({ children }: { children: React.ReactNode }) {
|
||||
const { width, height } = useRealWindowDimensions();
|
||||
|
||||
if (!IS_LOW_DENSITY) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={{ width, height, overflow: "hidden" }}>
|
||||
<View
|
||||
style={{
|
||||
width: width / DEX_SCALE,
|
||||
height: height / DEX_SCALE,
|
||||
transform: [{ scale: DEX_SCALE }],
|
||||
transformOrigin: "top left",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
67
utils/dexDimensionOverride.ts
Normal file
67
utils/dexDimensionOverride.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Samsung DeX 等の低密度ディスプレイで、Dimensions を仮想的に縮小する。
|
||||
* コンポーネントが小さい画面だと思ってレイアウトし、
|
||||
* App.tsx の DensityScaleWrapper が transform scale で実画面に拡大する。
|
||||
*
|
||||
* 必ず App.tsx の最初にインポートすること。
|
||||
*/
|
||||
import { Dimensions, PixelRatio } from "react-native";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
const pr = PixelRatio.get();
|
||||
export const IS_LOW_DENSITY = pr < 1.5;
|
||||
export const DEX_SCALE = IS_LOW_DENSITY ? Math.min(1.3, 1.5 / pr) : 1;
|
||||
|
||||
// オリジナル関数の参照を保存
|
||||
const originalGet = Dimensions.get.bind(Dimensions);
|
||||
const originalAddEventListener =
|
||||
Dimensions.addEventListener.bind(Dimensions);
|
||||
|
||||
if (IS_LOW_DENSITY) {
|
||||
// Dimensions.get('window') をスケーリング
|
||||
(Dimensions as any).get = (type: "window" | "screen") => {
|
||||
const real = originalGet(type);
|
||||
if (type === "window") {
|
||||
return {
|
||||
...real,
|
||||
width: real.width / DEX_SCALE,
|
||||
height: real.height / DEX_SCALE,
|
||||
};
|
||||
}
|
||||
return real;
|
||||
};
|
||||
|
||||
// change イベントもスケーリング
|
||||
(Dimensions as any).addEventListener = (type: string, handler: any) => {
|
||||
const wrappedHandler = (event: any) => {
|
||||
if (event?.window) {
|
||||
handler({
|
||||
...event,
|
||||
window: {
|
||||
...event.window,
|
||||
width: event.window.width / DEX_SCALE,
|
||||
height: event.window.height / DEX_SCALE,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
handler(event);
|
||||
}
|
||||
};
|
||||
return originalAddEventListener(type, wrappedHandler);
|
||||
};
|
||||
}
|
||||
|
||||
/** パッチ前の実画面サイズを取得するhook(DensityScaleWrapper用) */
|
||||
export function useRealWindowDimensions() {
|
||||
const [dims, setDims] = useState(() => {
|
||||
const d = originalGet("window");
|
||||
return { width: d.width, height: d.height };
|
||||
});
|
||||
useEffect(() => {
|
||||
const sub = originalAddEventListener("change", ({ window }: any) => {
|
||||
setDims({ width: window.width, height: window.height });
|
||||
});
|
||||
return () => sub?.remove();
|
||||
}, []);
|
||||
return dims;
|
||||
}
|
||||
Reference in New Issue
Block a user