Получить ширину CardView в Adapter.onBindViewHolder

У меня есть список постов, и большинство из них — изображения (проще говоря, это посты, как в приложениях G+ или FB). Каждая запись сообщения имеет соотношение сторон изображения, поэтому я могу установить высоту изображения в зависимости от его ширины еще до того, как изображение было загружено с сервера, чтобы макет карты не менялся при загрузке.

Проблема layout_width="match_parent" установлена ​​как для карточки, так и для пост-изображения. Когда я получаю ширину карты, она равна нулю. Поэтому я не могу рассчитать высоту.

На данный момент единственное решение, которое я вижу, - это взять ширину родительского контейнера (RecyclerView) и вычесть все отступы, но это не похоже на хорошее решение.

Есть ли другой способ сделать это?

Вот пример кода адаптера

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    ....
    int width = holder.itemView.getWidth();
    ....
    //do some calculations
}

Макеты (без ненужных частей)

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/card"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:descendantFocusability="blocksDescendants"
    android:foreground="?android:attr/selectableItemBackground"
    card_view:cardBackgroundColor="#ffffff"
    card_view:cardCornerRadius="3dp">

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <include
            android:id="@+id/includedPost"
            layout="@layout/post_details" />    
    </RelativeLayout>
</android.support.v7.widget.CardView>

включеноПост:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <ImageView
        android:id="@+id/postImage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/commenterImage"
        android:minHeight="120dp"
        android:scaleType="centerCrop" />

</RelativeLayout>

person Andrew    schedule 28.10.2014    source источник
comment
Можете ли вы поместить часть кода, где вы вызываете метод getWidth (с результатом 0)?   -  person Gaëtan Maisse    schedule 28.10.2014
comment
Обновлен мой вопрос с образцом кода   -  person Andrew    schedule 28.10.2014
comment
Возможно, вы могли бы попытаться вызвать метод getWidth() в onViewAttachedToWindow в соответствии с документом: это можно использовать как разумный сигнал о том, что пользователь собирается увидеть представление.   -  person Gaëtan Maisse    schedule 28.10.2014
comment
Только что попробовал. Это работает для представлений, которые переработаны, но вновь созданные имеют ширину, равную 0.   -  person Andrew    schedule 28.10.2014


Ответы (2)


когда вызывается onBind или onViewAttachedToWindow, дочерний элемент не еще не измерен, поэтому вы не можете получить ширину. Даже если эти вызовы были сделаны после измерения ребенка, то, что вы пытаетесь сделать, не будет хорошей практикой, поскольку изменение роста потребует нового измерения.

Если вы используете LinearLayoutManager, он даст полную ширину дочернему элементу (ожидайте заполнения RecyclerView и дочерних полей). Это не здорово, но можно вывести свой рост оттуда.

Другой (более гибкий) подход заключается в создании пользовательского ImageView, сохраняющего соотношение сторон. Когда вызывается onBind, вы устанавливаете желаемое соотношение сторон вашего пользовательского ImageView. Когда вызывается измерение, оно будет измеряться в зависимости от вашего соотношения сторон.

class MyImageView extends ImageVIew {
      ....
      private float mScale = 1f;

      public void setScale(int scale) {
          mScale = scale;
      }

      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

          super.onMeasure(widthMeasureSpec, heightMeasureSpec);

          int width = getMeasuredWidth();
          setMeasuredDimension(width, width * mScale);
      }
}

Таким образом, в вашем методе onBind вы вызываете setScale для ImageView в зависимости от вашего отношения w/h.

Я не проверял, но этот подход должен работать по желанию.

person yigit    schedule 28.10.2014
comment
действительно блестяще, но setMeasuredDimension немного сбивает с толку в вашем примере: int width = getMeasuredWidth(); int height = getMeasuredHeight(); setMeasuredDimension((int)(ширина * mScale), высота); - person cinatic; 03.07.2016

Для этого можно использовать Преобразование Пикассо.

Класс FitToTargetViewTransformation:

import android.graphics.Bitmap;
import android.view.View;

import com.squareup.picasso.Transformation;

/**
 * Picasso Transformation class to fit image to target View size
 */
public class FitToTargetViewTransformation implements Transformation {
  private View view;

  public FitToTargetViewTransformation(View view) {
    this.view = view;
  }

  @Override
  public Bitmap transform(Bitmap source) {
    int targetWidth = view.getWidth();

    double aspectRatio = (double) source.getHeight() / (double) source.getWidth();
    int targetHeight = (int) (targetWidth * aspectRatio);
    if (source.getHeight() >= source.getWidth()) {
      return source;
    }
    Bitmap result = Bitmap.createScaledBitmap(source, targetWidth, targetHeight, false);
    if (result != source) {
      // Same bitmap is returned if sizes are the same
      source.recycle();
    }
    return result;
  }

  @Override
  public String key() {
    return "transformation" + " desiredWidth";
  }
}

И где-то в onBindViewHolder вы делаете что-то вроде этого:

Picasso.with(context)
    .load(AVATAR_ENDPOINT)
    .transform(new FitToTargetViewTransformation(feedViewHolder.icAvatar))
    .into(feedViewHolder.icAvatar);
person localhost    schedule 28.10.2014
comment
Я не верю, что это решит мою проблему. У меня нет проблем с масштабированием изображений. Моя проблема заключается в настройках размера ImageView до того, как изображение было загружено, чтобы предотвратить дальнейшее изменение размеров карт при загрузке некоторых изображений. - person Andrew; 28.10.2014