I couldnt get the width in any of the startup methods as the layout isn't done.
The ViewTreeObserver helped me out, from the docs:
public ViewTreeObserver getViewTreeObserver ()
Since: API Level 1
Returns the ViewTreeObserver for this view's hierarchy. The view tree observer can be used to get notifications when global events, like layout, happen. The returned ViewTreeObserver observer is not guaranteed to remain valid for the lifetime of this View. If the caller of this method keeps a long-lived reference to ViewTreeObserver, it should always check for the return value of isAlive().
src/TestFragment.java
import android.app.Fragment;
import android.os.Bundle;
import android.view.*;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class TestFragment extends Fragment {
int fragmentWidth;
int fragmentHeight;
int calls;
int percent;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_one, null);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
percent = 100;
final View myView = getView();
final TextView tvOne = (TextView) myView.findViewById(R.id.tvOne);
final TextView tvTwo = (TextView) myView.findViewById(R.id.tvTwo);
final TextView tvThree = (TextView) myView.findViewById(R.id.tvThree);
final Button myButton = (Button) myView.findViewById(R.id.myButton);
myView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
calls++;
fragmentWidth = getView().getWidth();
fragmentHeight = getView().getHeight();
if(fragmentWidth > 0)
{
// Width greater than 0, layout should be done.
// Set the textviews and remove the listener.
tvOne.setText("Fragment Width: " + String.valueOf(fragmentWidth) + " Height: " + String.valueOf(fragmentHeight));
tvTwo.setText("Calls to onGlobalLayout: " + String.valueOf(calls));
getView().getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
});
myButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
percent = percent - 10;
// Need to switch FILL_PARENT to WRAP_CONTENT
// otherwise setWidth(pixels) doesn't work.
RelativeLayout.LayoutParams myParams
= new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
myParams.addRule(RelativeLayout.BELOW, R.id.myButton);
tvThree.setLayoutParams(myParams);
if(percent > 0)
{
tvThree.setWidth((fragmentWidth * percent)/100);
tvThree.setText(String.valueOf(percent) + "%");
}
else
{
percent = 100;
tvThree.setWidth(fragmentWidth);
tvThree.setText("100% again!");
}
}
});
}
}
layout/fragment_one.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView android:id="@+id/tvOne"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:color/holo_blue_dark"
android:layout_alignParentLeft="true"
/>
<TextView android:id="@+id/tvTwo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/tvOne"
/>
<Button android:id="@+id/myButton"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/tvTwo"
android:text="-10%" />
<TextView android:id="@+id/tvThree"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:color/holo_blue_dark"
android:text="100%"
android:gravity="right"
android:layout_alignParentLeft="true"
android:layout_below="@id/myButton"
/>
</RelativeLayout>
All seems well, the ViewTreeObserver.OnGlobalLayoutListener() gets the correct width and height as the screenshots prove.
What if we need the width in a custom adapter ? Doing the layout based on the data you set in it.
Let's create another fragment.
layout/fragment_two.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView android:id="@+id/myListView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
src/DataFragment.java
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Random;
public class DataFragment extends Fragment {
IntItemAdapter myAdapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_two, null);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ArrayList<Integer> myPercentInts = new ArrayList<Integer>();
Random myRandom = new Random();
for(int i = 0; i < 100; i++)
myPercentInts.add(myRandom.nextInt(100) + 1);
View myView = getView();
ListView myListView = (ListView) myView.findViewById(R.id.myListView);
myAdapter = new IntItemAdapter(getActivity(), R.layout.row_integer_layout, myPercentInts);
myListView.setAdapter(myAdapter);
myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
TextView testWidth = (TextView) view.findViewById(R.id.tvPercentUsed);
Log.d("TextView Width:", String.valueOf(testWidth.getWidth()));
}
});
}
public class IntItemAdapter extends ArrayAdapter<Integer>
{
private Activity context;
private int resourceID;
ArrayList<Integer> data = null;
int screenWidth;
public IntItemAdapter(Activity context, int resource, ArrayList<Integer> data)
{
super(context, resource, data);
this.context = context;
this.data = data;
this.resourceID = resource;
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View row = convertView;
if(screenWidth <= 0)
{
// Get the total width that ListView allocates.
screenWidth = parent.getWidth();
}
if(row == null)
{
LayoutInflater inflater = context.getLayoutInflater();
row = inflater.inflate(resourceID, parent, false);
}
Integer item = data.get(position);
if(item != null)
{
TextView itemPercentUsed = (TextView) row.findViewById(R.id.tvPercentUsed);
if(itemPercentUsed != null)
{
int newWidth = (screenWidth * item) / 100;
itemPercentUsed.setWidth(newWidth);
itemPercentUsed.setText(item.toString() + "%");
}
}
return row;
}
}
}
Works but doesnt look so nice code-wise, more elegant solution ? We could use the first solution instead and create the adapter when we know the true width of the fragment. That's if ListView is supposed to fill it all up or implement the ViewTreeObserver in the adapter.
Screenshots, both fragments visible: (Updated the DataFragment TextView background with a drawable)
Thank you very much)
SvaraRadera