使用了一段時間的react-native,它的UI設計方式真是酷炫,原來我是學android的,由於有android的開發基礎,所以我打算從java上學習react-native在原生代碼上的設計,並且寫下該文章筆記作爲以後的知識回顧。
- react-native版本 0.33.0
AndroidManifest.xml
...
<application
android:name=".MainApplication" //使用自己的Application
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity" // 初始Activity
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"> // 添加配置監聽
<intent-filter> // 隱式intent配置
<action android:name="android.intent.action.MAIN" /> // 決定應用程序最先啓動的Activity
<category android:name="android.intent.category.LAUNCHER" /> // 決定應用程序是否顯示在程序列表裏
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> // debug模式下react-native的工具Activity
</application>
</manifest>
MainApplication.java
// ReactApplication接口定義getReactNativeHost方法
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
// 開發者模式開關
@Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG; // 全局類BuildConfig,判斷是否爲debug模式
}
// 返回ReactPackage對象數組,
// ReactPackage定義基本的原生模塊和視圖管理
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
);
}
};
// ReactNativeHost控制一個ReactInstanceManager對象
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
}
MainActivity繼承com.facebook.react.ReactActivity,ReactActivity實現DefaultHardwareBackBtnHandler(機器返回按鈕接口,觸發JS中返回按鈕監聽事件),PermissionAwareActivity兩個接口,使用ReactActivityDelegate封裝主幹代碼的
com.facebook.react.ReactActivityDelegate
/**
* MainActivity的初始化
*/
public class ReactActivityDelegate {
private static final String REDBOX_PERMISSION_MESSAGE =
"Overlay permissions needs to be granted in order for react native apps to run in dev mode";
private final @Nullable Activity mActivity;
private final @Nullable FragmentActivity mFragmentActivity;
private final @Nullable String mMainComponentName; // app名
private @Nullable ReactRootView mReactRootView; // 根視圖
private @Nullable DoubleTapReloadRecognizer mDoubleTapReloadRecognizer; // 雙擊'R'鍵Reload JS
private @Nullable PermissionListener mPermissionListener;
...
protected void onCreate(Bundle savedInstanceState) {
if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {
// Get permission to show redbox in dev builds.
if (!Settings.canDrawOverlays(getContext())) {
Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
getContext().startActivity(serviceIntent);
FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
Toast.makeText(getContext(), REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
}
}
if (mMainComponentName != null) {
loadApp(mMainComponentName); // 創建ReactRootView
}
mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
}
protected void loadApp(String appKey) {
if (mReactRootView != null) {
throw new IllegalStateException("Cannot loadApp while app is already running.");
}
mReactRootView = createRootView();
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
appKey,
getLaunchOptions());
getPlainActivity().setContentView(mReactRootView);
}
...
private Context getContext() {
if (mActivity != null) {
return mActivity;
}
return Assertions.assertNotNull(mFragmentActivity);
}
// 返回Activity或FragmentActivity轉換後的Activity
private Activity getPlainActivity() {
return ((Activity) getContext());
}
}
ReactRootView繼承SizeMonitoringFrameLayout,基礎父類爲FrameLayout。
ReactRootView
...
/**
* 監聽窗口大小變化改變內部佈局,獲取觸摸事件遍歷子視圖觸發JSTouchDispatcher中的響應事件
* 重寫ViewGroup方法:
* onInterceptTouchEvent
* requestDisallowInterceptTouchEvent
* 重寫View方法:
* onTouchEvent
*/
public class ReactRootView extends SizeMonitoringFrameLayout implements RootView {
...
/**
* 獲取所有的屏幕觸摸事件
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
dispatchJSTouchEvent(ev);
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
dispatchJSTouchEvent(ev);
super.onTouchEvent(ev);
// 當沒有子類觸發時返回true
// 從而允許後續的觸摸
return true;
}
private void dispatchJSTouchEvent(MotionEvent event) {
...
// 所有事件統一通過JSTouchDispatcher發送觸摸事件給JS
mJSTouchDispatcher.handleTouchEvent(event, eventDispatcher);
}
...
}
JSTouchDispatcher
/**
* 觸摸事件和JS事件連接
*/
public class JSTouchDispatcher {
...
// 找出觸發事件的視圖ID
public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) {
//MotionEvent.ACTION_POINTER_DOWN 多點按下
int action = ev.getAction() & MotionEvent.ACTION_MASK; // 獲取action,添加&...支持多點觸摸
if (action == MotionEvent.ACTION_DOWN) {
// mTargetTag按鈕事件標識符,-1爲初始
if (mTargetTag != -1) {
FLog.e(
ReactConstants.TAG,
"Got DOWN touch before receiving UP or CANCEL from last gesture");
}
// 是否取消原生手勢
mChildIsHandlingNativeGesture = false;
// 查找觸發手勢的視圖ID
mTargetTag = TouchTargetHelper.findTargetTagAndCoordinatesForTouch(
ev.getX(),
ev.getY(),
mRootViewGroup,
mTargetCoordinates, // 儲存ev的x,y
null);
// 處罰獲取到的子類視圖ID,處罰內部的JS事件
eventDispatcher.dispatchEvent(
TouchEvent.obtain(
mTargetTag,
TouchEventType.START, // 說明是按下事件
ev,
mTargetCoordinates[0],
mTargetCoordinates[1],
mTouchEventCoalescingKeyHelper));
}
...後面的結構基本相同,主要是通過eventDispatcher.dispatchEvent觸發不同的JS事件
}
...
}
touch部分這裏只是簡單的介紹了下,詳細解析可以參考React-Native系列Android——Touch事件原理及狀態效果