浸淫Android这么多年,收敛君表示第一次碰到这么基础而又棘手的问题。
- 以下分析基于api23
- 4.4/5.0系统都有该问题
情景再现
MainActivity
public class TestListActivity extends Activity {
private ListView vList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b2);
List<Map<String, CharSequence>> data = new ArrayList<>();
Map<String, CharSequence> map = new HashMap<>();
map.put("title", "uuuuu\nuu");
data.add(map);
vList = (ListView) findViewById(R.id.list);
SimpleAdapter adapter = new SimpleAdapter(this,
data,
R.layout.item_list,
new String[]{"title"},
new int[]{android.R.id.text1}
);
vList.setAdapter(adapter);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="demo.com.demoapp.TestListActivity">
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
这个简单的显示一个ListView的小例子,初学Java时恐怕都写过。我们运行看看效果,为了看的明白,打开显示布局边界开关:
现在让我们点击EditText,打开软键盘:
这个小问题在这个例子中的表现还算温和,TextView的宽度只会抖动一次。但是在收敛君的工作中,每次切换软键盘都会出现!快跑!boss要发飙了!!!
刨根问底
这个小问题陪伴我两天两夜,其中的辛酸就不啰嗦了,且看把它扒光的结果吧。
先来看一段TextView.onMeasure
方法的片段:
//……此处省略几行
Layout mLayout = getLayout();
TextUtils.TruncateAt mEllipsize = getEllipsize();
if (mLayout != null && mEllipsize == null) {
des = desired(mLayout);
}
//……此处省略若干行
if (des < 0) {
des = (int) Math.ceil(Layout.getDesiredWidth(mTransformed, mTextPaint));
}
首次执行onMeasure
方法时,mLayout==null
,此时des
从第二段代码取值;当切换软键盘,页面重新布局,再次调用onMeasure
方法时,mLayout!=null
,此时des
从第一段代码取值。而des
就是宽度的值。
讲道理两段代码获取的尺寸应该是一样的,BUT!!!第一段代码会计算\n
也就是换行符的宽度,第二段代码不会。
这也就是今天收敛君想分享的Android系统的BUG。
了解了问题的根源,解决起来也就方便了。只要每次都从第二段代码取值就可以了。别问我为什么不从第一段代码取值,我肯定不会告诉你是因为第一次mLayout==null
!
简单的修改方法是每次执行onMeasure
方法时,都通过反射把mLayout
置为null
。
今天记录这么多,有什么错误的地方还请各位大大指正!
今天受同事提醒,因为mEllipsize == null
时才会执行第一段代码进行测量,所以直接给TextView设置ellipsize属性也可以解决。不过常在河边走,哪有不湿鞋,总有遗忘的时候,想一劳永逸的话,还是继承一下TextView吧,然后在内部设置ellipsize属性。