由于容纳 RN view 的外围控件不确定,同时又要与其他view协调好尺寸和布局。
所以 RN 对 RootView(包含了一个模块的所有布局)的处理挺有技巧,RN 中有如下调用逻辑:

ReactActivity.createRootView -> setContentView(mReactRootView)

ReactRootView.onMeasure()

ReactInstanceManagerImpl.attachMeasuredRootViewToInstance

UIManagerModule.addMeasuredRootView

得到宽高:

```java
final int width;
final int height;
// If LayoutParams sets size explicitly, we can use that. Otherwise get the size from the view.
if (rootView.getLayoutParams() != null &&
rootView.getLayoutParams().width > 0 &&
rootView.getLayoutParams().height > 0) {
width = rootView.getLayoutParams().width;
height = rootView.getLayoutParams().height;
} else {
width = rootView.getWidth();
height = rootView.getHeight();
}

rootView.setOnSizeChangedListener(
new SizeMonitoringFrameLayout.OnSizeChangedListener() {
@Override
public void onSizeChanged(final int width, final int height, int oldW, int oldH) {
getReactApplicationContext().runOnNativeModulesQueueThread(
new Runnable() {
@Override
public void run() {
updateRootNodeSize(tag, width, height);
}
});
}
});
```
通过这个 SizeMonitoringFrameLayout.OnSizeChangedListener 将 SizeMonitoringFrameLayout(实际扮演一个容器 ViewGroup 来监听布局的改变)的宽高传递给 css-layout 用于布局各个"盒"节点。

在开发包含c/c++本地代码的android项目中,通过gdb来调试代码是必不可少的前提。

android官方为此提供了ndk-gdb,看起来非常之nice。但个人在实践中发现还是有一系列问题需要记载下(ndk版本:r10e):

1) ndk-build NDK_DEBUG=1这个选项编译时要加上,一般将之定制在你的c/c++ builder中

2) 即便你按照1)做了,在项目根目录运行ndk-gdb的时候还是会报以下错误:

ERROR: Package faywong.github.io.mediakit is not debuggable ! You can fix that in two ways:

- Rebuilt with the NDK_DEBUG=1 option when calling 'ndk-build'.

- Modify your manifest to set android:debuggable attribute to "true",

then rebuild normally.

After one of these, re-install to the device!

然后我们乖乖地跑到AndroidManifest.xml里边去修改Application标签的debuggable属性,eclipse会提示你不能hardcode,可以通过如下方式设置下:

QQ20151012-1

3) 接下来还有可能会遇到如下问题:

ERROR: Could not find gdb.setup under ./libs/

这是由于ndk-gdb命令的bug带来的,它没有去参照ABI的不同设置去不同的目录下找gdb.setup文件(是一个脚本文件,帮你做一些繁琐的gdb server的启动,gdb client的启动和设置等任务)。

简单绕过这个错误的方式是将libs/{your abi, e.g. armeabi-v7a}里的gdb.setup直接拷贝至libs目录下

4) 在你跋山涉水,翻山越岭走了这么久之后,再次运行ndk-gdb,会出现以下惊喜:

ERROR: Non-debuggable application installed on the target device.

Please re-install the debuggable version!

#### 更新
现在可以结合gradle-experimental插件和ndk中搭载的lldb + android studio 2.0断点native代码了,虽然还不那么完善,bug多多,但是相比过去的ndk-gdb时代还是进步一点了。

这台红米Note1W上会有系统资源(res/drawable/btn_check_holo_light.xml)找不到的问题。备忘下。

```bash
0x10800de
java.lang.NullPointerException
at android.graphics.drawable.DrawableContainer$DrawableContainerState.addChild(DrawableContainer.java:584)
at android.graphics.drawable.StateListDrawable$StateListState.addStateSet(StateListDrawable.java:291)
at android.graphics.drawable.StateListDrawable.inflate(StateListDrawable.java:189)
at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java:937)
at android.graphics.drawable.Drawable.createFromXml(Drawable.java:877)
at android.content.res.Resources.createFromXml(Resources.java:2821)
at android.content.res.Resources.loadDrawable(Resources.java:2191)
at android.content.res.Resources.loadDrawable(Resources.java:2103)
at android.content.res.TypedArray.getDrawable(TypedArray.java:602)
at android.widget.CompoundButton.(CompoundButton.java:74)
at android.widget.CheckBox.(CheckBox.java:68)
at android.widget.CheckBox.(CheckBox.java:64)
at android.widget.CheckBox.(CheckBox.java:60)
```

mView是我们期望有动画效果的目标View

```java
FrameLayout.LayoutParams mParams = ... // 我们的目标 layout params
if (mTransitionTime > 0) {
ValueAnimator widthAnim = ValueAnimator.ofInt(0, mParams.width); // 从0变化到期望的宽度
widthAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// 在回调里我们踩着节拍来修改 view 的 layout params
int val = (Integer) valueAnimator.getAnimatedValue();
mParams.width = val;
if (mView != null) {
mView.setLayoutParams(mParams);
}
}
});
widthAnim.setDuration(mTransitionTime);
widthAnim.start();

ValueAnimator heightAnim = ValueAnimator.ofInt(0, mParams.height); // 从0变化到期望的高度
heightAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// 在回调里我们踩着节拍来修改 view 的 layout params
int val = (Integer) valueAnimator.getAnimatedValue();
mParams.height = val;
if (mView != null) {
mView.setLayoutParams(mParams);
}
}
});
heightAnim.setDuration(mTransitionTime);
heightAnim.start();
}

if (mTransformDegree > 0) { // 如果需要,在由小变大的过程中添加旋转效果
ObjectAnimator.ofFloat(mView, "rotation", 0f, mTransformDegree * 1.0f)
.setDuration(mTransitionTime > 0 ? mTransitionTime : 0).start();
}
```

由于MEIZU手机的特殊性,对于NavigationBar的获取有特殊的讲究,有三个方面的因素:

1)MEIZU早期的系统(flyme os 5以下)存在smartbar,且可以设置智能隐藏;flyme os 5及以后smartbar被废弃,而使用android的NavigationBar

2)flymeos 5以下的系统有些存在系统dimen资源用于获取smartbar高度,有些则不再存在

3)是否智能隐藏SmartBar的开关并不与Android是否显示NavigationBar的开关为同一个

所以我今天研究了下,封装了一个方法:

```java
public static int getNavigationBarHeight(Context context) {
final boolean isMeiZu = Build.MANUFACTURER.equals("Meizu");

final boolean autoHideSmartBar = Settings.System.getInt(context.getContentResolver(),
"mz_smartbar_auto_hide", 0) == 1;

if (isMeiZu) {
if (autoHideSmartBar) {
return 0;
} else {
try {
Class c = Class.forName("com.android.internal.R$dimen");
Object obj = c.newInstance();
Field field = c.getField("mz_action_button_min_height");
int height = Integer.parseInt(field.get(obj).toString());
return context.getResources().getDimensionPixelSize(height);
} catch (Throwable e) { // 不自动隐藏smartbar同时又没有smartbar高度字段供访问,取系统navigationbar的高度
return getNormalNavigationBarHeight(context);
}
}
} else {
return getNormalNavigationBarHeight(context);
}
}

protected static int getNormalNavigationBarHeight(final Context ctx) {
try {
final Resources res = ctx.getResources();
int rid = res.getIdentifier("config_showNavigationBar", "bool", "android");
if (rid > 0) {
boolean flag = res.getBoolean(rid);
if (flag) {
int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
return res.getDimensionPixelSize(resourceId);
}
}
}
} catch (Throwable e) {
LogCatLog.d("FBTools", "getNormalNavigationBarHeight() exception:" + e.getMessage());
}
return 0;
}
```