动态调整 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 文件的预定义高度,并且在图像完成加载后立即过渡到我们计算的新高度。一旦该行出现在屏幕上,这会导致该行下方的全部内容突然移动。此外,我们从底部滚动到顶部(这是一个聊天线程)。

我尝试过的事情:

  1. 我们尝试通过 "setSelection" 方法(从 ListView)滚动整个 ListView 到 "preload" 用户看到内容之前的内容。那没有用,而且在我们滚动浏览它们的短暂时间内似乎没有加载图像。

  2. 我们已经尝试设置静态 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 中观察到任何跳动!