动态调整 ListView 行的大小
Resizing ListView Rows Dynamically
概览: 我有一个包含不同类型行的 ListView,包括不同高度的图像和文本。在运行时,我试图根据行的宽度动态设置包含图像的布局的高度。因此行的宽度设置为 match_parent(因为我们希望它填充设备的宽度),但必须在运行时计算高度以获得框架的特定纵横比。
这是我正在使用的适配器的代码片段:
@Override
public View getView(View convertView) {
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.chat_row_image, null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
ViewTreeObserver observer = holder.videoFrame.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
frameWidth = holder.container.getWidth();
int width = frameWidth;
int height = (int) (frameWidth / Utilities.MEDIA_CELL_ASPECT_RATIO);
Log.d(TAG, "Calculated : " + width + "," + height);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, height);
holder.messageImage.setLayoutParams(params);
holder.videoFrame.getViewTreeObserver().removeGlobalOnLayoutListener(
this);
Log.d(TAG, "Imageview : " + holder.messageImage.getWidth() + "," + holder.messageImage.getHeight());
}
});
return convertView;
}
问题:此代码运行良好,但在滚动 ListView 期间,存在 UI 很容易注意到的故障。主要的 UI 错误是行具有来自 XML 文件的预定义高度,并且在图像完成加载后立即过渡到我们计算的新高度。一旦该行出现在屏幕上,这会导致该行下方的全部内容突然移动。此外,我们从底部滚动到顶部(这是一个聊天线程)。
我尝试过的事情:
我们尝试通过 "setSelection" 方法(从 ListView)滚动整个 ListView 到 "preload" 用户看到内容之前的内容。那没有用,而且在我们滚动浏览它们的短暂时间内似乎没有加载图像。
我们已经尝试设置静态 XML 文件宽度和高度,这解决了问题,但没有考虑到不同的屏幕尺寸。
我需要什么帮助:我们希望即使在动态调整布局大小时也能保持滚动流畅。最后的办法是为每个屏幕尺寸类别创建不同的 XML 文件并对高度进行硬编码,但这不会像我们希望的那样保持框架的纵横比。
如果您需要任何说明,请告诉我。提前致谢!
你可以在 chat_row_image.xml 的外部添加一个 LinearLayout,像这样
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/layout_ll
android:layout_width="wrap_content"
android:layout_height="wrap_content">
[...]
</LinearLayout>
</RelativeLayout>
在Adapter.java
@Override
public View getView(View convertView) {
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.chat_row_image, null);
holder = new ViewHolder(convertView);
LinearLayout ll = (LinearLayout) view.findViewById(R.id.layout_ll);
LayoutParams linearParams = (LayoutParams) ll.getLayoutParams();
linearParams.width = width;
linearParams.height = height;
ll.setLayoutParams(linearParams);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
return convertView;
}
答案是在 Adapter 的构造函数中设置框架的尺寸,而不是 getView()
方法。根据南诺雷的建议,我们没有使用ViewTreeObserver.OnGlobalLayoutListener()
,而是根据显示器的宽度和其他布局元素的padding/margin来计算框架的尺寸。
public ImageChatRow(Context context) {
this.mContext = context;
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
displayWidth = size.x - 40; //padding is 40 in our case
displayHeight = (int) (displayWidth / Utilities.MEDIA_CELL_ASPECT_RATIO);
}
然后在getView()
方法中,我们在inflating view后直接设置frame的尺寸。
public View getView(View convertView) {
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.chat_row, null);
holder = new ViewHolder(convertView);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(displayWidth, displayHeight);
layoutParams.gravity = Gravity.CENTER;
holder.frame.setLayoutParams(layoutParams);
convertView.setTag(holder);
} else
holder = (ViewHolder) convertView.getTag();
这解决了加载行和动态设置它们的高度时出现故障的问题。自从实施此代码以来,我们没有在 ScrollView 中观察到任何跳动!