1

I have a fragment inside a ViewPager and am trying to dynamically change the height of a ListView depending on the size of the screen.

Here is the xml code for my fragment:

Fragment.xml

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <RelativeLayout
        android:id="@+id/rlDiscoveredDevice"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/tvSuggestBTOn">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/tvDiscoveredDevices"
            android:layout_alignParentTop="true"
            android:layout_alignParentStart="true"
            android:text="@string/text_list_discovered_devices"
            />

        <ProgressBar
            android:id="@+id/pbDiscoveredDevices"
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toEndOf="@+id/tvDiscoveredDevices"
            android:layout_marginStart="16dp"
            />

        <ListView
            android:id="@+id/lstDiscoveredBTDevices"
            android:layout_height="250dp"
            android:layout_width="wrap_content"
            android:divider="@android:color/transparent"
            android:dividerHeight="@dimen/list_view_divider_height"
            android:choiceMode="singleChoice"
            android:listSelector="@color/list_item_selected"
            android:layout_below="@+id/tvDiscoveredDevices"
            android:layout_marginTop="@dimen/list_view_margin_top"
            />

    </RelativeLayout>

    <ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/pairBT"
        android:background="@drawable/ic_action_down"
        android:layout_marginStart="134dp"
        android:layout_below="@+id/rlDiscoveredDevice"
        android:layout_alignParentStart="true"
        />

    <ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/unpairBT"
        android:background="@drawable/ic_action_up"
        android:layout_below="@+id/rlDiscoveredDevice"
        android:layout_toEndOf="@+id/pairBT"
        android:layout_marginStart="73dp"
         />

    <RelativeLayout
        android:id="@+id/rlPairedDevice"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/pairBT">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/tvPairedDevices"
            android:text="@string/text_list_paired_devices"
            android:layout_alignParentStart="true"
            />

        <ListView
            android:id="@+id/lstPairedBTDevices"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:divider="@android:color/transparent"
            android:dividerHeight="@dimen/list_view_divider_height"
            android:choiceMode="singleChoice"
            android:listSelector="@color/list_item_selected"
            android:layout_below="@+id/tvPairedDevices"
            android:layout_alignParentStart="true"
            android:layout_marginTop="@dimen/list_view_margin_top"
            />

    </RelativeLayout>

</RelativeLayout>

Here is my java code that I use to dynamically change the height:

DiscoveredDevice.java

public class DiscoveredDevice extends Fragment{
    final String TAG = "DiscoverDevice Fragment";
    private SharedPreferences appPrefs;
    private BTActions btActions;
    private ArrayList<BluetoothDevice> arrDiscoveredDevicesList;
    private Set<BluetoothDevice> arrPairedDevicesList;
    private ArrayAdapter<String> btDiscListArrayAdapter;
    private ArrayAdapter<String> btPairedListArrayAdapter;
    private String strDiscoveredListItemSelected = "";
    private String strPairedListItemSelected = "";
    private CommonFunctions cf;
    private boolean blnIsFragmentLoaded = false;

    // UI Objects
    private TextView tvDiscoveredDevices;
    private TextView tvPairedDevices;
    private ListView lvDiscoveredList;
    private ListView lvPairedDevicesList;
    private ImageButton ibtnPair;
    private ImageButton ibtnUnPair;
    private ProgressBar pbDiscDevicesSpinner;
    private TextView tvSuggestBTOn;
    private ProgressBar pbLoading;

    public DiscoveredDevice() {
        btActions = new BTActions();
        cf = new CommonFunctions();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "Begin render of Discovered Device fragment...");
        super.onCreate(savedInstanceState);

        // Define variables
        appPrefs = this.getActivity().getPreferences(Context.MODE_PRIVATE);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_discovered_device, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // Define the lists on DiscoveredDevice fragment
        btDiscListArrayAdapter = new ArrayAdapter<>(getContext(), R.layout.simple_row, R.id.simple_row_Txt);
        btPairedListArrayAdapter = new ArrayAdapter<>(getContext(), R.layout.simple_row, R.id.simple_row_Txt);

        // Define UI Objects
        defineUIObjects();

        // Position UI objects
        positionUIObjects();
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
    }

    private void defineUIObjects() {
        tvDiscoveredDevices = (TextView) getView().findViewById(R.id.tvDiscoveredDevices);
        tvPairedDevices = (TextView) getView().findViewById(R.id.tvPairedDevices);
        lvDiscoveredList = (ListView) getView().findViewById(R.id.lstDiscoveredBTDevices);
        lvPairedDevicesList = (ListView) getView().findViewById(R.id.lstPairedBTDevices);
        ibtnPair = (ImageButton) getView().findViewById(R.id.pairBT);
        ibtnUnPair = (ImageButton) getView().findViewById(R.id.unpairBT);
        tvSuggestBTOn = (TextView) getView().findViewById(R.id.tvSuggestBTOn);
        pbDiscDevicesSpinner = (ProgressBar) getView().findViewById(R.id.pbDiscoveredDevices);

        pbLoading = (ProgressBar) getView().findViewById(R.id.spin_kit_progress);
        pbLoading.setIndeterminateDrawable(new DoubleBounce());
    }

    private void positionUIObjects() {
        final ViewGroup vgDiscDevice = (ViewGroup) getView().findViewById(R.id.rlDiscoveredDevice);
        final AtomicInteger aiLayoutHeight = new AtomicInteger();

        Rect rect = new Rect();

        // Get the window
        Window win = getActivity().getWindow();
        win.getDecorView().getWindowVisibleDisplayFrame(rect);

        // Find height of AppBarLayout
        AppBarLayout ablTabs = (AppBarLayout) getActivity().findViewById(R.id.ablTabs);

        // Obtain the screen height & width
        DisplayMetrics metrics = new DisplayMetrics();
        getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
        int intScreenHeight = metrics.heightPixels;
        int intScreenWidth = metrics.widthPixels;
        Log.i(TAG, "Actual Screen Height = " + intScreenHeight + " Width = " + intScreenWidth);

        // Set the height for Discovered Devices list
        RelativeLayout.LayoutParams rlParams = (RelativeLayout.LayoutParams) getView().findViewById(R.id.rlDiscoveredDevice).getLayoutParams();

        // Get height of Discovered Devices relative layout
        int intDiscoveredDevicesRLHeight = (int)(Math.round((intScreenHeight - rect.top - ablTabs.getMeasuredHeight()) * 0.45));
        Log.i(TAG, "Setting the height of Discovered Devices Relative layout as '" + intDiscoveredDevicesRLHeight + "'");
        rlParams.topMargin = ablTabs.getMeasuredHeight();
        rlParams.leftMargin = 50; // I DID THIS JUST TO CHECK IF THE LEFT MARGIN GETS MOVED TO THE RIGHT. THIS IS WHERE I NEED A BETTER WAY TO PROPERLY ALIGN THE LIST
        rlParams.height = intDiscoveredDevicesRLHeight;

        lvDiscoveredList.setLayoutParams(rlParams);

    }

I want each list to occupy 45% of the screen(excluding the AppBarLayout). If you see the below screenshot, when I set the new height, the ListView goes out of alignment and part of it gets cuts to the left of the screen. I have set the Left margin to 50 to bring it into view.

[Screenshot]

enter image description here

I have placed 2 ListViews inside a RelativeLayouts so that they can be individually controlled as a whole. Am I doing something wrong here ?

AyeVeeKay
  • 149
  • 1
  • 12

1 Answers1

0

I think you can achieve this with just xml, no dynamic resizing!

In your post you mentioned that you want the list views to each take 45% of the available height. Thus, I am assuming that the center content with the image views will take 10% of the height (though this approach will also work if they had a fixed height, I'll include that answer too at the bottom.

All you need to do is change your top level layout to a LinearLayout (with a vertical orientation), put your image views inside of a LinearLayout (with horizontal orientation), change some of the heights of your views and add layout_weight attributes to the two RelativeLayouts and the inner LinearLayout you will create. Here is an example of what your xml will look like:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <RelativeLayout
        android:id="@+id/rlDiscoveredDevice"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_above="@+id/tvSuggestBTOn"
        android:layout_weight="9">

        <TextView
            android:id="@+id/tvDiscoveredDevices"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="true"
            android:layout_alignParentTop="true"
            android:text="@string/text_list_discovered_devices"
            />

        <ProgressBar
            android:id="@+id/pbDiscoveredDevices"
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_toEndOf="@+id/tvDiscoveredDevices"
            />

        <ListView
            android:id="@+id/lstDiscoveredBTDevices"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@+id/tvDiscoveredDevices"
            android:divider="@android:color/transparent"
            android:dividerHeight="@dimen/list_view_divider_height"
            android:choiceMode="singleChoice"
            android:listSelector="@color/list_item_selected"
            android:background="@drawable/abc_list_selector_disabled_holo_dark"
            />

    </RelativeLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:gravity="center"
        android:layout_weight="2">

        <ImageButton
            android:id="@+id/pairBT"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/ic_action_down"
            />

        <ImageButton
            android:id="@+id/unpairBT"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/ic_action_up"/>
    </LinearLayout>

    <RelativeLayout
        android:id="@+id/rlPairedDevice"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="9">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/tvPairedDevices"
            android:text="@string/text_list_paired_devices"
            android:layout_alignParentStart="true"
            />

        <ListView
            android:id="@+id/lstPairedBTDevices"
            android:layout_height="match_parent"
            android:layout_width="match_parent"
            android:divider="@android:color/transparent"
            android:dividerHeight="@dimen/list_view_divider_height"
            android:choiceMode="singleChoice"
            android:listSelector="@color/list_item_selected"
            android:layout_below="@+id/tvPairedDevices"
            android:layout_alignParentStart="true"
            android:background="@drawable/abc_list_selector_disabled_holo_dark"
            />

    </RelativeLayout>

</LinearLayout>

The important thing to note here is that the RelativeLayouts now have a weight of 9 each and the center linear layout has a weight of 2. Thus the space in the top level linear layout will be divided in that ratio, the RelativeLayouts will get 45% each and the LinearLayout will get 10%.

If you wanted the linear layout in the center to be wrap content instead of taking 10% of the screen (I would recommend this) then you could go ahead and assign it a height of wrap_content and remove the layout_weight attribute from it. The top level LinearLayout will then take the leftover height after allocating space for the center LinearLayout and divide it evenly between the two Relative layouts.

PS: (Heads up, you can probably use the xml I posted here. I set backgrounds on the list views to make it easy for me to see their sizes without data, make sure to remove those).

PPS: Note that this approach allows you to remove a lot of the layout positioning attributes that you had with a top level relative layout! This not only improves the cleanliness of your code, but also makes your UI more performant (Relative Layouts are less performant than other view groups, especially when nested).

Community
  • 1
  • 1
Dr. Nitpick
  • 1,662
  • 1
  • 12
  • 16