I've made a construction in Android to easy build & show a DialogFragment on the screen. The dialog contains a TextView, ListView and a Button. It's build to easily inflate different views in the ListView so I can make custom Dialogs.
The left image is correct. On the right I inflated an extra view. A view I use to display text in the DialogFragment. But when I add that view it doesn't wrap content anymore. The ListView makes itself scrollable.
I can add more of the bluebuttons you see on the image and the ListView will still wrap_content correct. But when I add one or more of the other views it the ListView makes itself scrollable.
The problem is I just can't get the ListView to wrap_content correct.
This is the xml of the "blue button" view: (the correct inflated view)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/overlay_item_label"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical"
android:weightSum="1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/tile_button_categorytile"
android:orientation="horizontal">
<ImageView
android:id="@+id/popUpImage"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="6dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_speaker" />
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/popUpButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="4dp"
android:layout_marginRight="15dp"
android:layout_weight="1"
android:padding="4dp"
android:text="Bewerken {fa-android}"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
The view used as text in the DialogFragment: (which causes the ListView to make itself scrollable)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/overlay_item_label"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/white"
android:layout_weight="1"
android:weightSum="1">
<TextView
android:id="@+id/LabelOverlay"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_gravity="center"
android:gravity="center"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:text="@string/placeholder"
android:textColor="@color/purple"
android:textSize="12sp" />
</LinearLayout>
When I add extra paddingTop and paddingBottom to the view that is used to show text, the ListView doesn't make itself scrollable (on only a few devices). The padding is set to around 20dp and then it on some devices it doesn't make itself scrollable, but then on other devices its too much padding so it shows whitespace...
This is the code of the fragment that shows the DialogFragment:
public class PopUpFragmentPhone extends DialogFragment {
PopUp popUpData;
ListView listView;
public static PopUpFragmentPhone newInstance(PopUp data) {
PopUpFragmentPhone popUpFragment = new PopUpFragmentPhone();
Bundle args = new Bundle();
args.putParcelable("PopUpData", data);
popUpFragment.setArguments(args);
return popUpFragment;
}
@Override
public void onStart(){
super.onStart();
Window window = getDialog().getWindow();
WindowManager.LayoutParams windowParams = window.getAttributes();
windowParams.dimAmount = 0.70f;
windowParams.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
window.setAttributes(windowParams);
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
getDialog().getWindow().setLayout(MainApplication.width / 2, ViewGroup.LayoutParams.WRAP_CONTENT);
getDialog().setCanceledOnTouchOutside(true);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NO_TITLE, android.R.style.Theme);
popUpData = getArguments().getParcelable("PopUpData"); //get the PopUp class.
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.pop_up_phone, container, false);
//Set the title of the popUp
TextView popUpTitle = (TextView)v.findViewById(R.id.overlayTitle);
popUpTitle.setText(popUpData.getHeader());
listView = (ListView) v.findViewById(R.id.listViewItemsOverlay);
listView.setAdapter(new PopUpAdapterPhone(MainApplication.getContext(), 1, popUpData.getItems(), this));
boolean showCancel = popUpData.isCancel();
Button cancelButton = (Button) v.findViewById(R.id.cancel_button);
Drawable cancelIcon = new IconDrawable(getActivity(), FontAwesomeIcons.fa_times).colorRes(R.color.red).sizeDp(20);
cancelButton.setCompoundDrawablesWithIntrinsicBounds(cancelIcon, null, null, null);
if(showCancel) {
// Cancel button click
cancelButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
getDialog().dismiss();
}
});
} else {
cancelButton.setVisibility(View.GONE);
}
getDialog().getWindow().setLayout(MainApplication.width / 2, ViewGroup.LayoutParams.WRAP_CONTENT);//set the size of the fragment, so the rest is a touch to cancel the popUP
return v;
}
}
The view of the pop up fragment:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="5dp"
android:weightSum="3"
android:background="@drawable/tile_button_wordtile"
android:id="@+id/overlay_master_view">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu"
android:id="@+id/overlayTitle"
android:textStyle="bold"
android:textSize="15sp"
android:padding="4dp"
android:textColor="@color/purple"
android:layout_weight="1"
android:background="@color/white"
android:layout_margin="4dp"
android:layout_gravity="center_horizontal" />
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
android:layout_weight="1"
android:divider="@android:color/transparent"
android:dividerHeight="5.0sp"
android:id="@+id/listViewItemsOverlay">
</ListView>
<com.joanzapata.iconify.widget.IconButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:padding="5dp"
android:layout_weight="1"
android:textStyle="bold"
android:textSize="15sp"
android:drawablePadding="5dp"
android:id="@+id/cancel_button"
android:layout_marginTop="5dp"
android:textColor="@color/red"
android:background="@drawable/tile_button_functiontile"
android:text="@string/cancel"/>
</LinearLayout>
I tried lots of things, inflate it in a LinearLayout or RelativeLayout. Wrap_content, match_parent, weightSum and all that kinds of stuff. Tried changing the layouts in a lot of ways but it all didn't work out.
I hope someone can help me.
Note: In the code below, I've removed the backgrounds as I didn't have them.
But basically you shouldn't have to change it too much to match what you want.
Your dialog
import android.app.Dialog;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.View;
import android.view.WindowManager;
import android.widget.ListView;
import java.util.ArrayList;
public class CustomDialogFragment extends DialogFragment {
private static final String DIALOG_DATA = "my.app.package.DIALOG_DATA";
public static CustomDialogFragment newInstance(ArrayList<ListElement> data) {
CustomDialogFragment f = new CustomDialogFragment();
Bundle args = new Bundle();
args.putParcelableArrayList(DIALOG_DATA, data);
f.setArguments(args);
return f;
}
@Override
public void onResume() {
super.onResume();
// Calculate the height/width you want
// See: https://stackoverflow.com/a/12923805/1827254 for more info
// In this example, the width is not changed as retrieved from the LayoutParams of the dialog.
WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
int width = params.width; //getResources().getDimensionPixelSize(R.dimen.popup_width);
int height = getResources().getDimensionPixelSize(R.dimen.popup_height);
getDialog().getWindow().setLayout(width, height);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
ArrayList<ListElement> data = getArguments().getParcelableArrayList(DIALOG_DATA);
// Inflate the dialog layout
View view = View.inflate(getContext(), R.layout.custom_dialog, null);
ListView listView = view.findViewById(R.id.dialog_list);
listView.setAdapter(new CustomAdapter(getContext(), data));
View btn = view.findViewById(R.id.dialog_cancel_button);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dismiss(); // Dismiss the dialog.
}
});
return new AlertDialog.Builder(getActivity())
.setView(view)
.create();
}
}
The dialog layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/overlay_master_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp">
<!-- Title of the view -->
<!-- Note: instead of using 'android:text="@string/placehoder"' use
'tools:text="My preview text"'. Otherwise the system has to set your
placeholder value and then replace it with the actual value you want in the code -->
<TextView
android:id="@+id/dialog_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="@color/white"
android:padding="4dp"
android:text="@string/menu"
android:textColor="@color/purple"
android:textSize="15sp"
android:textStyle="bold"
tools:text="Preview text" />
<!-- The ListView needs to have a height of 0 in order to let
the button to be displayed below it-->
<ListView
android:id="@+id/dialog_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:divider="@android:color/transparent"
android:dividerHeight="5.0sp"
android:scrollbars="none" />
<!-- Button 'cancel'-->
<com.joanzapata.iconify.widget.IconButton
android:id="@+id/dialog_cancel_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:drawablePadding="5dp"
android:gravity="start"
android:padding="5dp"
android:text="@string/cancel"
android:textColor="@color/red"
android:textSize="15sp"
android:textStyle="bold" />
</LinearLayout>
The list adapter
class CustomAdapter extends ArrayAdapter<ListElement> {
private final LayoutInflater mInflater;
public CustomAdapter(Context context, ArrayList<ListElement> data) {
super(context, 0, data);
mInflater = LayoutInflater.from(context);
}
@NonNull
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
// Get the data item for this position
ListElement element = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
// Use your custom layout
convertView = mInflater.inflate(R.layout.list_element, parent, false);
}
// Get the views and set the data
TextView tv = (TextView) convertView.findViewById(R.id.elt_text);
tv.setText(element.getText());
ImageView imageView = (ImageView) convertView.findViewById(R.id.elt_image);
imageView.setImageResource(element.getIconRes());
return convertView;
}
}
List element layout
I think setting the weight to every component was one of your issues
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/overlay_item_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/blue"
android:orientation="horizontal">
<ImageView
android:id="@+id/elt_image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="6dp"
android:scaleType="fitCenter"
tools:src="@mipmap/ic_launcher" />
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/elt_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="4dp"
android:layout_marginRight="15dp"
android:padding="4dp"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold"
tools:text="Bewerken {fa-android}" />
</LinearLayout>
An element of the list
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.DrawableRes;
/**
* Every element of the list has an icon and a text.
* You can add other parameters
*/
public class ListElement implements Parcelable {
private String mText;
@DrawableRes
private Integer mIconRes;
public ListElement() {
// Default constructor
}
public ListElement(String text, @DrawableRes int resId) {
this.mText = text;
this.mIconRes = resId;
}
protected ListElement(Parcel in) {
mText = in.readString();
if (in.readByte() == 0) {
mIconRes = null;
} else {
mIconRes = in.readInt();
}
}
public static final Creator<ListElement> CREATOR = new Creator<ListElement>() {
@Override
public ListElement createFromParcel(Parcel in) {
return new ListElement(in);
}
@Override
public ListElement[] newArray(int size) {
return new ListElement[size];
}
};
public String getText() {
return mText;
}
public Integer getIconRes() {
return mIconRes;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(mText);
if (mIconRes == null) {
parcel.writeByte((byte) 0);
} else {
parcel.writeByte((byte) 1);
parcel.writeInt(mIconRes);
}
}
}
Use case example (in MainActivity)
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ArrayList<ListElement> data = generateData();
// Create and show the dialog.
DialogFragment newFragment = CustomDialogFragment.newInstance(data);
newFragment.show(ft, "dialog");
}
private ArrayList<ListElement> generateData() {
ArrayList<ListElement> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(new ListElement("elt " + i, R.mipmap.ic_launcher));
}
return list;
}
}