Reputation: 408
Overview: I have a ListView of different types of rows including images and text of varying heights. At runtime, I am trying to dynamically set the heights of the layout containing the image based on the width of the row. So the row's width is set to match_parent (because we want it to fill the width of the device), but the height must be calculated at runtime to get a specific aspect ratio for the frame.
Here's the code snippet from the Adapter that I'm using:
@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;
}
The problem: this code works well but during scrolling through the ListView, there are UI glitches that are readily noticeable. The main UI bug is that the rows have a pre-defined height from the XML file and are instantly transitioning to the new height we've calculated once the image finishes loading. This causes the entirety of contents below that row to suddenly shift as soon as the row appears on the screen. Also, we're scrolling from the bottom to the top (this is a chat thread).
Things I've tried:
We tried scrolling through the entire ListView via "setSelection" method (from ListView) to "preload" the contents before the user sees them. That didn't work and it doesn't seem like the images are loaded during the brief time we're scrolling past them.
We've tried setting a static XML file width and height, and that fixes the issue, but doesn't account for different screen sizes.
What I need help with: We want to keep the scrolling smooth even while resizing the layout dynamically. The final resort would be to create different XML files for each screen size category and hard code the height, but that would not keep our frame's aspect ratio as we desire.
Please let me know if there are any clarifications you would want. Thanks in advance!
Upvotes: 1
Views: 2478
Reputation: 408
The answer was to set the dimensions of the frame in the constructor of the Adapter instead of the getView()
method. Based on Nannuo Lei's suggestion, we are not using the ViewTreeObserver.OnGlobalLayoutListener()
, instead we are calculating the dimensions of the frame based on the display's width and padding/margin of other layout elements.
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);
}
Then in the getView()
method, we just set the dimensions of the frame directly after inflating the view.
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();
This solves the problems of the glitches in loading rows and dynamically setting their heights. We haven't observed any jumpiness in the ScrollView since implementing this code!
Upvotes: 4
Reputation: 1
You can add a LinearLayout outside in chat_row_image.xml, like this
<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>
In 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;
}
Upvotes: 0