有谁知道为什么,和/或如何解决这个问题?看起来,在配置更改之后,屏幕尺寸的变化可能会发生很多时候 – 即在调用onConfigurationChanged时配置更改后,尺寸是否不正确?
以下是以下代码的调试输出,在从纵向到横向旋转之后显示onLayout / onSizeChanged调用(请注意,设备为540×960,因此横向宽度应为960,而纵向宽度为540):
03-13 17:36:21.140: DEBUG/RotateTest(27765): onConfigurationChanged: LANDSCAPE 03-13 17:36:21.169: DEBUG/RotateTest(27765): onSizeChanged:540,884,0 03-13 17:36:21.189: DEBUG/RotateTest(27765): onLayout:true-0,540,884 03-13 17:36:21.239: DEBUG/RotateTest(27765): onSizeChanged:960,464,884 03-13 17:36:21.259: DEBUG/RotateTest(27765): onLayout:true-0,960,464
还要注意的是,第一个onSizeChanged oldwidth和oldheight为0,表示我们刚刚添加到视图层次结构 – 但是具有错误的横向尺寸!
这里是说明这个行为的代码:
MyActivity.java
package com.example; import android.app.Activity; import android.content.res.Configuration; import android.os.Bundle; import android.util.Log; import android.widget.FrameLayout; public class MyActivity extends Activity { private static String TAG = "RotateTest"; @Override public void onConfigurationChanged(Configuration newConfig) { Log.d(TAG,"onConfigurationChanged: " + (newConfig.orientation == 1 ? "PORTRAIT" : "LANDSCAPE")); super.onConfigurationChanged(newConfig); _setView(); } @Override public void onCreate(Bundle savedInstanceState) { Log.d(TAG,"onCreate"); super.onCreate(savedInstanceState); _setView(); } private void _setView() { MyHorizontalScrollView scrollView = new MyHorizontalScrollView(this,null); setContentView(scrollView); } }
MyHorizontalScrollView.java
package com.example; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.widget.HorizontalScrollView; public class MyHorizontalScrollView extends HorizontalScrollView { private static String TAG = "RotateTest"; public MyHorizontalScrollView(Context context,AttributeSet attrs) { super(context,attrs); } @Override protected void onLayout(boolean changed,int l,int t,int r,int b) { super.onLayout(changed,l,t,r,b); Log.d(TAG,"onLayout:" + String.format("%s-%d,%d,%d",changed,b)); } @Override protected void onSizeChanged(int w,int h,int oldw,int oldh) { super.onSizeChanged(w,h,oldw,oldh); Log.d(TAG,"onSizeChanged:" + String.format("%d,w,oldh)); } }
AndroidManifest.xml中
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="9"/> <application android:label="@string/app_name" > <activity android:name="MyActivity" android:label="@string/app_name" android:configChanges="orientation" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
解决方法
我回答这个问题的方式 – 因为我相信答案是,这取决于 – 通过在requestLayout方法中添加一个try / catch或logging语句.这样,您可以查看何时进行重新测量和重新布置的请求,以及在尝试/捕获的情况下由谁执行.
Android中的布局方式是将您的视图标记为具有requestLayout的脏布局.总是在一段时间内在UI线程上运行的Android玩家将会在未来的某个不确定点重新设计和重新布置树中已经被标记为脏的视图.
我冒昧地猜测,onConfigurationChanged,你正在得到几个requestLayout调用,而looper正在调用onMeasure在其中的某个地方.
这就是我的日志记录:
11-07 15:39:13.624: W/YARIAN(30006): requestLayout 11-07 15:39:13.632: W/YARIAN(30006): requestLayout 11-07 15:39:13.640: W/YARIAN(30006): requestLayout 11-07 15:39:13.647: W/YARIAN(30006): requestLayout 11-07 15:39:13.686: W/YARIAN(30006): requestLayout 11-07 15:39:13.718: W/YARIAN(30006): requestLayout 11-07 15:39:13.827: W/YARIAN(30006): requestLayout 11-07 15:39:14.108: W/YARIAN(30006): onLayout 11-07 15:39:14.155: W/YARIAN(30006): requestLayout 11-07 15:39:14.272: W/YARIAN(30006): onLayout
The Android documentation has more information在测量和铺设时,可悲的是我上面描述的细节很短.
Event Handling and Threading
The basic cycle of a view is as follows:
- An event comes in and is dispatched to the appropriate view. The view handles the event and notifies any listeners.
- If in the course of processing the event,the view’s bounds may need to be changed,the view will call requestLayout().
- Similarly,if in the course of processing the event the view’s appearance may need to be changed,the view will call invalidate().
- If either requestLayout() or invalidate() were called,the framework will take care of measuring,laying out,and drawing the
tree as appropriate.Note: The entire view tree is single threaded. You must always be on the UI thread when calling any method on any view. If you are doing work on other threads and want to update the state of a view from that thread,you should use a Handler.