Related
I am trying to create a custom button with progress bar inside of it in Android.
The button should have 2 states:
Normal and Loading.
In Normal state it should show a text while in Loading state it should show a centerred circular progress indicator instead of the text!
When the button state returns to "Normal" state it should show the text again.
To achieve this, I've thought about create a custom view which build from a RelativeLayout
and inside of it there is a TextView and a Circular progress indicator and change their visibility in code according to the state.
This idea and logic works pretty good.
Please refer to images of my buttons with the progress indicators:
However, the problem comes when I want to apply a selector to this view,
I've created a style and a selector for each button but it just not setting the right background to the view when its disabled.
A RelativeLayout doesn't has an enabled attribute available in its xml so I had to add a styleable attr and change its state in code with isEnabled = false or something like that.
This makes it disabled in did, but the background stays as it is enabled (The selector not working).
This is my "Button" source code:
class ProgressButton #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr) {
private val progressBar: LottieAnimationView
private val buttonTextView: TextView
init {
val root = LayoutInflater.from(context).inflate(R.layout.progress_button, this, true)
buttonTextView = root.findViewById(R.id.button_text)
progressBar = root.findViewById(R.id.progress_indicator)
loadAttr(attrs, defStyleAttr)
}
private fun loadAttr(attrs: AttributeSet?, defStyleAttr: Int) {
val arr = context.obtainStyledAttributes(
attrs,
R.styleable.ProgressButton,
defStyleAttr,
0
)
val buttonText = arr.getString(R.styleable.ProgressButton_text)
val loading = arr.getBoolean(R.styleable.ProgressButton_loading, false)
val enabled = arr.getBoolean(R.styleable.ProgressButton_enabled, true)
isEnabled = enabled
arr.recycle()
buttonTextView.text = buttonText
setLoading(loading)
}
fun setLoading(loading: Boolean){
if(loading){
buttonTextView.visibility = View.GONE
progressBar.visibility = View.VISIBLE
} else {
buttonTextView.visibility = View.VISIBLE
progressBar.visibility = View.GONE
}
}
}
This its layout:
<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="50dp">
<TextView
android:id="#+id/button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textAppearance="?android:attr/textAppearanceButton"
android:text="OK" />
<com.airbnb.lottie.LottieAnimationView
android:id="#+id/progress_indicator"
android:layout_width="#dimen/progressbar_width"
android:layout_height="#dimen/progressbar_width"
android:layout_centerInParent="true"
android:visibility="gone"
app:lottie_autoPlay="true"
app:lottie_loop="true"
app:lottie_rawRes="#raw/lottile_button_loader" />
</RelativeLayout>
This a the background with selector for it:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="#dimen/components_corner_radius" />
<solid android:color="#color/button_black_bg_selector" />
</shape>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#color/black" android:state_enabled="true" />
<item android:color="#color/buttons_black_pressed" android:state_pressed="true" />
<item android:color="#color/buttons_black_disabled" android:state_enabled="false" />
<item android:color="#color/black" />
This is the styling and theme:
<style name="Theme.Widget.ProgressButton" parent="">
<item name="android:textAppearanceButton">#style/TextAppearance.Body.White</item>
</style>
<style name="Widget.ProgressButton.Black" parent="#style/Theme.Widget.ProgressButton">
<item name="android:colorControlHighlight">#color/buttons_black_pressed</item>
<item name="android:background">#drawable/progress_button_black</item>
</style>
And finally, this how i use it in a fragment layout xml:
<com.example.widgets.ProgressButton
android:id="#+id/button_black_loading"
android:theme="#style/Widget.ProgressButton.Black". //This is where it gets its style and theme
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="16dp"
app:loading="true"/>
Any help will be appreciated.
Finally I've came to a pattern that seems to work with a single style line in the custom view usage xml (e.g. Fragment or Activity layout).
For each button I've defined similar styling blocks that looks like this:
<style name="Theme.Widget.Button.Black" parent="">
<item name="android:textAppearanceButton">#style/TextAppearance.BlackButton</item> //This will set the theme for the button internal TextView
<item name="android:colorControlHighlight">#color/buttons_black_pressed</item> //This will set the highlight color for ripple effect
</style>
<style name="Widget.Button.Black" parent="">
**<item name="android:theme">#style/Theme.Widget.Button.Black</item>** //Note this theme attribute which takes that above styling and applying it as a theme!!
<item name="android:background">#drawable/button_black</item> //A shape with selector drawable
</style>
The background drawable:
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight"> //This comes from the theme for the ripple effect
<item android:drawable="#drawable/button_black_shape"/> //The selector
</ripple>
The background shape drawable:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="#dimen/components_corner_radius" />
<solid android:color="#color/button_black_bg_selector" />
</shape>
The selector:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#color/black" android:state_enabled="true" />
<item android:color="#color/buttons_black_pressed" android:state_pressed="true" />
<item android:color="#color/buttons_black_disabled" android:state_enabled="false" />
<item android:color="#color/black" />
</selector>
And finally using the button in fragment XML like this:
<com.sample.widgets.ProgressButton
android:id="#+id/button_black_enabled"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="16dp"
app:enabled="true"
app:text="OK"
style="#style/Widget.Button.Black"/> //The style brings a theme also!
After adding the selector you need to change the state of ProgressButtonI am adding essential code below which should work.
Selector should be like :-
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/black" android:state_enabled="true" />
<item android:drawable="#color/buttons_black_pressed" android:state_pressed="true" />
<item android:drawable="#color/buttons_black_disabled" android:state_enabled="false" />
and ProgressButton.kt
class ProgressButton #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr) {
private val progressBar: LottieAnimationView
private val buttonTextView: TextView
init {
val root = LayoutInflater.from(context).inflate(R.layout.progress_button, this, true)
buttonTextView = root.findViewById(R.id.button_text)
progressBar = root.findViewById(R.id.progress_indicator)
loadAttr(attrs, defStyleAttr)
}
private fun loadAttr(attrs: AttributeSet?, defStyleAttr: Int) {
// this line can be removed if you are setting selector in xml
setBackgroundResource(R.drawable.button_selector)
setLoading(true)
}
fun setLoading(loading: Boolean) {
if (loading) {
buttonTextView.visibility = View.GONE
progressBar.visibility = View.VISIBLE
} else {
buttonTextView.visibility = View.VISIBLE
progressBar.visibility = View.GONE
}
isEnabled = !loading
}
}
Google recently released the android.support.design.widget.NavigationView widget as part of the com.android.support:design:22.2.0 library, which greatly simplified (and standardises) the process of creating a NavigationDrawer.
However according to the design specs, the list item should be Roboto Medium, 14sp, 87% #000000. The NavigationView exposes no textSize or textStyle to customise this.
What are my options if I'm pedantic about maintaining the correct design specifications using the Google provided NavigationView (or customising it in any other way)?
Create new style at the file app/src/main/res/values/styles.xml
<style name="NavigationDrawerStyle">
<item name="android:textSize">20sp</item><!-- text size in menu-->
<!-- item size in menu-->
<item name="android:listPreferredItemHeightSmall">40dp</item>
<item name="listPreferredItemHeightSmall">40dp</item>
<!-- item padding left in menu-->
<item name="android:listPreferredItemPaddingLeft">8dp</item>
<item name="listPreferredItemPaddingLeft">8dp</item>
<!-- item padding right in menu-->
<item name="android:listPreferredItemPaddingRight">8dp</item>
<item name="listPreferredItemPaddingRight">8dp</item>
</style>
Add it to your main_layout.xml
<android.support.design.widget.NavigationView
...
app:theme="#style/NavigationDrawerStyle"
....>
</android.support.design.widget.NavigationView>
All params of the navigation items (which you can change) are located here (path to file ...\sdk\extras\android\support\design\res\layout\design_navigation_item.xml )
design_navigation_item.xml
<android.support.design.internal.NavigationMenuItemView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeightSmall"
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
android:paddingRight="?attr/listPreferredItemPaddingRight"
android:drawablePadding="#dimen/navigation_icon_padding"
android:gravity="center_vertical|start"
android:maxLines="1"
android:fontFamily="sans-serif-thin"
android:textSize="22sp"
android:textAppearance="?attr/textAppearanceListItem" />
Also you can override *.xml file (copy file from ...\sdk\extras\android\support\design\res\layout\design_navigation_item.xml), just in your app/src/main/res/layout folder create a layout named the same design_navigation_item.xml.
All layouts which can be Overriden are located here ...\sdk\extras\android\support\design\res\layout\
design_layout_snackbar.xml
design_layout_snackbar_include.xml
design_layout_tab_icon.xml
design_layout_tab_text.xml
design_navigation_item.xml
design_navigation_item_header.xml
design_navigation_item_separator.xml
design_navigation_item_subheader.xml
design_navigation_menu.xml
[UPDATE] Each version of com.android.support:design-{version} lib has different items to override.
Check all what you need in
[UPDATE 04/14/2020]
If you are using com.google.android.material.navigation.NavigationView
then open the class, and you will see:
public NavigationView(#NonNull Context context, #Nullable AttributeSet attrs) {
this(context, attrs, R.attr.navigationViewStyle);
}
So you can use attr navigationViewStyle to set your own style for the NavigationView via theme of your app:
NB: parent theme of AppTheme should be Theme.MaterialComponents
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar.Bridge">
...
<item name="navigationViewStyle">#style/AppNavigationViewStyle</item>
...
</style>
<style name="AppNavigationViewStyle" parent="Widget.MaterialComponents.NavigationView">
<item name="itemTextAppearance">#style/AppNavigationViewItemTextAppearance</item>
</style>
<style name="AppNavigationViewItemTextAppearance" parent="#style/TextAppearance.MaterialComponents.Subtitle2">
<item name="android:textSize">18sp</item>
</style>
Just open parent theme to see all <item name attrs for override
Since Android Support Library 22.2.1, Google has changed default textSize of items in NavigationView from 16sp to 14sp, which suits Material Design guideline well. However, in some cases(for example, when you want to support Chinese language), it seems larger textSize is better. Solution is simple:
add app:theme="#style/yourStyle.Drawer" to your NavigationView in your layout.xml
in styles.xml, add android:textSize="16sp" in style yourStyle.Drawer
You can customize everything from text color to size and font in your style.xml
<style name="NavDrawerTextStyle" parent="Base.TextAppearance.AppCompat">
<item name="android:textColor">#color/colorPrimaryDark</item>
<item name="android:textSize">16sp</item>
<item name="android:textStyle">bold</item>
</style>
and then in your NavigationView:
<android.support.v4.widget.DrawerLayout 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:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<android.support.design.widget.NavigationView
...
android:itemTextAppearance="#style/NavDrawerTextStyle"
/>
you can use this attributes inside xml file
app:itemTextAppearance="?android:attr/textAppearanceMedium"
or for small text
app:itemTextAppearance="?android:attr/textAppearance"
or for large text
app:itemTextAppearance="?android:attr/textAppearanceLarge"
This worked for me:
activity_main.xml
<android.support.design.widget.NavigationView
android:id="#+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:theme="#style/NavigationDrawerStyle"
app:headerLayout="#layout/navdrawer_header"
app:menu="#menu/navdrawer_menu" />
styles.xml
<style name="NavigationDrawerStyle">
<item name="android:textSize">16sp</item>
</style>
Scouring through the source code I found this layout file
/platform/frameworks/support/.../design/res/layout/design_drawer_item.xml
with the following attribute
<android.support.design.internal.NavigationMenuItemView
...
android:textAppearance="?attr/textAppearanceListItem"
Which meant all we had to do was to override the textAppearanceListItem style in our project.
res/values/styles.xml
<style name="AppTheme" ... >
...
<item name="textAppearanceListItem">#style/list_item_appearance</item>
</style>
<style name="list_item_appearance">
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">sans-serif-medium</item>
</style>
I'm not totally sure what else this will affect but if anyone has a better answer I'd be happy to accept that instead!
Maybe it might help. Recently I had to do this programmatically.
I used this class:
public class CustomTypefaceSpan extends TypefaceSpan {
private final Typeface newType;
private final float newSp;
public CustomTypefaceSpan(String family, Typeface type, float sp) {
super(family);
newType = type;
newSp = sp;
}
#Override
public void updateDrawState(TextPaint ds) {
applyCustomTypeFace(ds, newType, newSp);
}
#Override
public void updateMeasureState(TextPaint paint) {
applyCustomTypeFace(paint, newType, newSp);
}
private static void applyCustomTypeFace(Paint paint, Typeface tf, float sp) {
int oldStyle;
Typeface old = paint.getTypeface();
if (old == null) {
oldStyle = 0;
} else {
oldStyle = old.getStyle();
}
int fake = oldStyle & ~tf.getStyle();
if ((fake & Typeface.BOLD) != 0) {
paint.setFakeBoldText(true);
}
if ((fake & Typeface.ITALIC) != 0) {
paint.setTextSkewX(-0.25f);
}
paint.setTextSize(sp);
paint.setTypeface(tf);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<CustomTypefaceSpan> CREATOR = new Parcelable.Creator<CustomTypefaceSpan>() {
#Override
public CustomTypefaceSpan createFromParcel(Parcel in) {
return null;
}
#Override
public CustomTypefaceSpan[] newArray(int size) {
return new CustomTypefaceSpan[size];
}
};
}
Then I used like this:
// This is for color
SpannableString s = new SpannableString(item.getTitle().toString());
s.setSpan(new ForegroundColorSpan(Color.RED), 0, s.length(), 0);
// This is for typeface and size
Typeface typeFace = Functions.getTypeface(this, "Avenir");
if (typeFace != null) {
int size = 19;
float scaledSizeInPixels = size * getResources().getDisplayMetrics().scaledDensity;
CustomTypefaceSpan spanTypeFace = new CustomTypefaceSpan(item.getTitle().toString(), typeFace, scaledSizeInPixels);
s.setSpan(spanTypeFace, 0, s.length(), 0);
}
item.setTitle(s);
Hope this helps.
Goto activity_main.xml and select nav_view in design and you can change menu item text size by updating itemTextAppearance and itemTextColor like as follows
For com.google.android.material.navigation.NavigationView you should use the app namespace with your custom style:
app:itemTextAppearance="#style/NavDrawerTextStyle"
List Adapter Layout of your Navigation bar:
<?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">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:orientation="horizontal"
android:background="#drawable/pressed_state">
<TextView
android:id="#+id/textView_list_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/imageView_icons"
android:textStyle="bold"
android:layout_marginLeft="10dp"
android:textColor="#color/white"
android:textSize="17sp" />
<ImageView
android:id="#+id/list_adapter_image"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:visibility="visible"
android:layout_marginRight="50dp"
android:background="#drawable/circle_orange"/>
</RelativeLayout>
<LinearLayout
android:layout_height="1dp"
android:layout_width="match_parent"
android:background="#EC1294"></LinearLayout>
</LinearLayout>
This param in RelativeLayout set the background color --> android:background="#drawable/pressed_state"
Make this "pressed_state.xml" in drawable folder.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#color/light_blue" android:state_pressed="true"/>
Excuse me for my english.
Open or create values\dimens.xml
Add this code:
<dimen name="design_bottom_navigation_text_size" tools:override="true">11sp</dimen>
<dimen name="design_bottom_navigation_active_text_size" tools:override="true">12sp</dimen>
It should work
I have a custom BttomSheetDialogFragment and I want to have round corners in top of Bottom View
This is my Custom class that inflates my layout that I want to appear from bottom
View mView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.charge_layout, container, false);
initChargeLayoutViews();
return mView;
}
and also I have this XML resource file as background:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<corners android:topRightRadius="35dp"
android:topLeftRadius="35dp"
/>
<solid android:color="#color/white"/>
<padding android:top="10dp"
android:bottom="10dp"
android:right="16dp"
android:left="16dp"/>
</shape>
The problem is, when I set this resource file as background of my Layout's root element, the corners still are not rounded.
I can't use below code:
this.getDialog().getWindow().setBackgroundDrawableResource(R.drawable.charge_layout_background);
Because it overrides the default background of BottomSheetDialog and there won't be any semi-transparent gray color above my Bottom View.
Create a custom drawable rounded_dialog.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#android:color/white"/>
<corners android:topLeftRadius="16dp"
android:topRightRadius="16dp"/>
</shape>
Then override bottomSheetDialogTheme on styles.xml using the drawable as background:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="bottomSheetDialogTheme">#style/AppBottomSheetDialogTheme</item>
</style>
<style name="AppBottomSheetDialogTheme"
parent="Theme.Design.Light.BottomSheetDialog">
<item name="bottomSheetStyle">#style/AppModalStyle</item>
</style>
<style name="AppModalStyle"
parent="Widget.Design.BottomSheet.Modal">
<item name="android:background">#drawable/rounded_dialog</item>
</style>
This will change all the BottomSheetDialogs of your app.
With the new Material Component library you can customize the shape of your component using the shapeAppearanceOverlay attribute in your style (Note: it requires at least the version 1.1.0)
Just use the BottomSheetDialogFragment overriding the onCreateView method and then define your custom style for Bottom Sheet Dialogs.
Define the bottomSheetDialogTheme attribute in styles.xml in your app theme:
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
....
<item name="bottomSheetDialogTheme">#style/CustomBottomSheetDialog</item>
</style>
Then just define your favorite shape with shapeAppearanceOverlay
<style name="CustomBottomSheetDialog" parent="#style/ThemeOverlay.MaterialComponents.BottomSheetDialog">
<item name="bottomSheetStyle">#style/CustomBottomSheet</item>
</style>
<style name="CustomBottomSheet" parent="Widget.MaterialComponents.BottomSheet">
<item name="shapeAppearanceOverlay">#style/CustomShapeAppearanceBottomSheetDialog</item>
</style>
<style name="CustomShapeAppearanceBottomSheetDialog" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSizeTopRight">16dp</item>
<item name="cornerSizeTopLeft">16dp</item>
<item name="cornerSizeBottomRight">0dp</item>
<item name="cornerSizeBottomLeft">0dp</item>
</style>
You can obtain the same behavior overriding this method in your BottomSheetDialogFragment (instead of adding the bottomSheetDialogTheme in your app theme):
#Override public int getTheme() {
return R.style.CustomBottomSheetDialog;
}
In this case you are using this themeOverlay only in the single BottomSheetDialogFragment and not in all the app.
Important note about the EXPANDED STATE:
In the expanded state the BottomSheet has flat corners . You can check the official comment in github repo:
Our design team is strongly opinionated that rounded corners indicate scrollable content while flat corners indicate that there is no additional content. As such, they do no want us to add this change with fitToContents.
This behavior is provided by the BottomSheetBehavior and it is impossible to override it.
However there is a workaround -> DISCLAIMER: it can stop to work in the next releases !!
You can add a BottomSheetCallback in the BottomSheetDialogFragment:
#NonNull #Override public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
((BottomSheetDialog)dialog).getBehavior().addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
#Override public void onStateChanged(#NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
//In the EXPANDED STATE apply a new MaterialShapeDrawable with rounded cornes
MaterialShapeDrawable newMaterialShapeDrawable = createMaterialShapeDrawable(bottomSheet);
ViewCompat.setBackground(bottomSheet, newMaterialShapeDrawable);
}
}
#Override public void onSlide(#NonNull View bottomSheet, float slideOffset) {
}
});
return dialog;
}
#NotNull private MaterialShapeDrawable createMaterialShapeDrawable(#NonNull View bottomSheet) {
ShapeAppearanceModel shapeAppearanceModel =
//Create a ShapeAppearanceModel with the same shapeAppearanceOverlay used in the style
ShapeAppearanceModel.builder(getContext(), 0, R.style.CustomShapeAppearanceBottomSheetDialog)
.build();
//Create a new MaterialShapeDrawable (you can't use the original MaterialShapeDrawable in the BottoSheet)
MaterialShapeDrawable currentMaterialShapeDrawable = (MaterialShapeDrawable) bottomSheet.getBackground();
MaterialShapeDrawable newMaterialShapeDrawable = new MaterialShapeDrawable((shapeAppearanceModel));
//Copy the attributes in the new MaterialShapeDrawable
newMaterialShapeDrawable.initializeElevationOverlay(getContext());
newMaterialShapeDrawable.setFillColor(currentMaterialShapeDrawable.getFillColor());
newMaterialShapeDrawable.setTintList(currentMaterialShapeDrawable.getTintList());
newMaterialShapeDrawable.setElevation(currentMaterialShapeDrawable.getElevation());
newMaterialShapeDrawable.setStrokeWidth(currentMaterialShapeDrawable.getStrokeWidth());
newMaterialShapeDrawable.setStrokeColor(currentMaterialShapeDrawable.getStrokeColor());
return newMaterialShapeDrawable;
}
The BottomSheetDialog is setting a default white background color , this is why the corners are not visible, In order to show them you need to make the background of the dialog transparent by overriding the style of the BottomSheetDialog.
Define this style In your res/values/styles/styles.xml
<style name="BottomSheetDialog" parent="Theme.Design.Light.BottomSheetDialog">
<item name="bottomSheetStyle">#style/bottomSheetStyleWrapper</item>
</style>
<style name="bottomSheetStyleWrapper" parent="Widget.Design.BottomSheet.Modal">
<item name="android:background">#android:color/transparent</item>
</style>
And set this style to your BottomSheetDialog
View view = getLayoutInflater().inflate(R.layout.chooser_bottom_sheet, null);
BottomSheetDialog dialog = new BottomSheetDialog(this,R.style.BottomSheetDialog); // Style here
dialog.setContentView(view);
dialog.show();
create a shape named rounded_corners_shape
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="8dp"
android:topRightRadius="8dp"/>
<solid android:color="#color/white"/>
</shape>
define a style
<style name="AppBottomSheetDialogTheme"
parent="Theme.Design.Light.BottomSheetDialog">
<item name="bottomSheetStyle">#style/AppModalStyle</item>
</style>
<style name="AppModalStyle" parent="Widget.Design.BottomSheet.Modal">
<item name="android:background">#drawable/rounded_corners_shape</item>
</style>
use this style on your custom BottomSheetDialogFragment like this, it will be work!
public class CustomDialogFragment extends BottomSheetDialogFragment {
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(STYLE_NORMAL, R.style. AppBottomSheetDialogTheme);
}
...
}
This worked for me.
Create a background drawable (e.g. named shape_rounded_dialog):
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#color/color_white" />
<corners android:topLeftRadius="16dp"
android:topRightRadius="16dp" />
</shape>
Add the styles below:
<style name="AppBottomSheetDialogTheme"
parent="Theme.MaterialComponents.Light.BottomSheetDialog">
<item name="bottomSheetStyle">#style/CustomBottomSheetStyle</item>
</style>
<style name="CustomBottomSheetStyle"
parent="Widget.Design.BottomSheet.Modal">
<item name="android:background">#drawable/shape_rounded_dialog</item>
</style>
In your DialogFragment, override the method getTheme() to return your style.
#Override
public int getTheme() {
return R.style.AppBottomSheetDialogTheme;
}
The simplest and cleanest solution, that worked for me, was to put the following 4 lines in onViewCreated(View view, Bundle savedInstanceState) method of my fragment class:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
View bottomSheet = (View) view.getParent();
bottomSheet.setBackgroundTintMode(PorterDuff.Mode.CLEAR);
bottomSheet.setBackgroundTintList(ColorStateList.valueOf(Color.TRANSPARENT));
bottomSheet.setBackgroundColor(Color.TRANSPARENT);
}
This will allow for your custom drawable with rounded corners to be properly shown once set as the background of the top level view of your fragment layout.
In essence this overrides the default BottomSheetFragment attributes regarding color, tintMode and tintList.
Using this, there is no need for messing with style resources.
If you use the last version of material component you just have to override ShapeAppearance.MaterialComponents.LargeComponent (as the bottom sheet use this shape) and set the value you want like :
<style name="ShapeAppearance.YourApp.LargeComponent" parent="ShapeAppearance.MaterialComponents.LargeComponent">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">12dp</item>
</style>
And then set in your app style :
<item name="shapeAppearanceLargeComponent">#style/ShapeAppearance.YourApp.LargeComponent</item>
The solution of Gabriele Mariotti is similar and works too but this one is simpler.
I was checking the same thing today and yes you were right about following code
this.getDialog().getWindow().setBackgroundDrawableResource(R.drawable.charge_layout_background);
this applies to fragment background, so instead you should get the bottomsheet view from dialog window and change the background here is the code
#SuppressLint("RestrictedApi")
#Override
public void setupDialog(Dialog dialog, int style) {
super.setupDialog(dialog, style);
View rootView = getActivity().getLayoutInflater().inflate(R.layout.view_member_info,null,false);
unbinder = ButterKnife.bind(this, rootView);
adjustUIComponents();
dialog.setContentView(rootView);
FrameLayout bottomSheet = (FrameLayout) dialog.getWindow().findViewById(android.support.design.R.id.design_bottom_sheet);
bottomSheet.setBackgroundResource(R.drawable.container_background);
}
here bottomsheet is the actual view you want to change.
Answer by Koma Yip from another question worked for me, you should try it.
Create a xml in drawable , say dialog_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#color/white"/>
<corners android:radius="30dp" />
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" />
</shape>
put this in your layout xml root node:
set it as the background in your layout xml
android:background="#drawable/dialog_bg"
and in onCreateView() put this:
set the background of your dialog to transparent
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
Step 1:
Create a res/drawable named as rounded_background.xml :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="32dp"
android:topRightRadius="32dp" />
<solid android:color="#D81B60" />
</shape>
Step 2:
Create this style to remove the dialog background:
<style name="NoBackgroundDialogTheme" parent="Theme.AppCompat.Light.Dialog">
<item name="android:windowBackground">#null</item>
</style>
Step 3:
Set the drawable to the root view of the dialog using setBackgroundResource() & set the style by overriding getTheme() method
Java:
public class MyDialogFragment extends BottomSheetDialogFragment {
#Override
public int getTheme() {
return R.style.NoBackgroundDialogTheme;
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = View.inflate(requireContext(), R.layout.bottom_sheet_profile, null);
view.setBackgroundResource(R.drawable.rounded_background);
return view;
}
}
Kotlin:
class MyDialogFragment : BottomSheetDialogFragment() {
override fun getTheme() = R.style.NoBackgroundDialogTheme
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val view: View = View.inflate(requireContext(), R.layout.bottom_sheet_profile, null)
view.setBackgroundResource(R.drawable.rounded_background)
return view
}
}
Result:
if( view.setBackgroundResource(R.drawable.rounded_background)) this line dose'nt work then try to set the background of the Xml format of fragment.
Create a shape drawable .. which we will use as background for bottom sheet .
Provide the appropriate value for radius of top left and right corner .
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="24dp"
android:topRightRadius="24dp" />
<padding android:top="2dp" />
<solid android:color="#color/white" />
</shape>
Now create style for " Bottom sheet dialog fragment "
<style name="BottomSheet" parent="#style/Widget.Design.BottomSheet.Modal">
<item name="android:background">#drawable/drawable_bottomsheet_background</item>
</style>
<style name="BaseBottomSheetDialog" parent="#style/Theme.Design.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="bottomSheetStyle">#style/BottomSheet</item>
</style>
<style name="BottomSheetDialogTheme" parent="BaseBottomSheetDialog" />
Now create a custom class that will extend BottomSheetDilogFragment ,where you provide your style .
open class CustomRoundBottomSheet : BottomSheetDialogFragment() {
override fun getTheme(): Int = R.style.BottomSheetDialogTheme
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = BottomSheetDialog(requireContext(), theme)
}
Now use this class wherever you want to have round corner bottom sheet .
eg
class BottomSheetSuccess : CustomRoundBottomSheet() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.bottomsheet_shopcreate_success, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}
Simplistic solution:
class TopRoundedCornersFragment : BottomSheetDialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, R.style.AppBottomSheetDialogTheme)
}
}
In styles.xml
<style name="BottomSheetStyle" parent="Widget.Design.BottomSheet.Modal">
<item name="android:background">#drawable/bottom_sheet_dialog_bg</item>
</style>
<style name="AppBottomSheetDialogTheme" parent="Theme.Design.Light.BottomSheetDialog">
<item name="bottomSheetStyle">#style/BottomSheetStyle</item>
</style>
Last, create a top rounded corner drawable resource (bottom_sheet_dialog_bg.xml)
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#android:color/white" />
<corners
android:topLeftRadius="4dp"
android:topRightRadius="4dp" />
</shape>
I know this question already has an accepted answer. I'd like to document the problems I went through and how I finally got it working so it is useful for someone on the future.
Firstly, I was using Theme.AppCompat.Light.DarkActionBar as the parent for our AppTheme. This meant #Gabriele Mariotti solution kept crashing with the error Could not inflate Behavior subclass com.google.android.material.bottomsheet.BottomSheetBehavior. I fixed this by simply changing the parent to Theme.MaterialComponents.Light.DarkActionBar. This did not affect our theme in any way but the RTE was gone. You can also fix this issue by simply including the require items to your style. But I didn't bother figuring out which styles were required by BottomSheetBehavior.
Secondly, try as I might, but I couldn't get the actual Frame layout (which was the BottomSheetDialogFragment) uses to have round corners. I realized that setting this to an image Drawable worked but not with a shape or a #null. Turns out, it was because the LinearLayout I was using had a background defined. This was overriding any background in the style. Removing that finally resulted in rounded corners.
Also, I didn't require any background shape to be set to round the corners. #Gabriele Mariotti's solution worked as soon as I made the above changes. However, to set the background colour what I wanted it to be, I had to override the "backgroundTint" item.
PS: I'm new to Android dev, and am maintaining an old App that was made for internal use in our College. I'm not all that familiar with Android's layout system or with the material library. I guess that's why it took me 3 days to figure this out. I hope this is useful to someone in the future.
This answer is only for the issue of setting the background color to Color.TRANSPARENT after setting up a drawable with rounded background to the layout.
None of the answer worked for me to set the background color to Color.TRANSPARENT except overriding setupDialog() solution:
#Override
public void setupDialog(Dialog dialog, int style) {
super.setupDialog(dialog, style);
View contentView = View.inflate(getContext(),
R.layout.fragment_bottom_sheet, null);
dialog.setContentView(contentView);
...
((View) contentView.getParent()).setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
}
BUT the contentView you set for dialog here is not the view you get in onViewCreated() when inflating in onCreateView(). It breaks the standard flow, so may issue troubles like you can't use View Bindings - Kotlin Android Extensions in onViewCreated()
So I tweak a bit to set the background in onActivityCreated():
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
(view?.parent as View).setBackgroundColor(Color.TRANSPARENT)
}
Hope this help who got same trouble
Bottom Sheet Dialog with Curved shape and peek height
<!-- BottomSheet Dialog -->
<style name="BottomSheetDialog" parent="Theme.Design.Light.BottomSheetDialog">
<item name="bottomSheetStyle">#style/CustomBottomSheet</item>
</style>
<style name="CustomBottomSheet" parent="Widget.MaterialComponents.BottomSheet">
<item name="shapeAppearanceOverlay">#style/CustomShapeAppearanceBottomSheetDialog</item>
<item name="behavior_peekHeight">420dp</item>
</style>
<style name="CustomShapeAppearanceBottomSheetDialog" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSizeTopRight">20dp</item>
<item name="cornerSizeTopLeft">20dp</item>
<item name="cornerSizeBottomRight">0dp</item>
<item name="cornerSizeBottomLeft">0dp</item>
</style>
As pointed in other answers, when state is BottomSheetBehavior.STATE_EXPANDED corners will be flattened.
You can overcome this issue by setting peekHeight property of BottomSheetBehavior and using your custom style.
abstract class BaseBottomSheetFragment : BottomSheetDialogFragment(){
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (state == BottomSheetBehavior.STATE_EXPANDED) {
val displayMetrics = DisplayMetrics()
requireActivity().windowManager!!.defaultDisplay!!.getMetrics(displayMetrics)
(dialog as BottomSheetDialog).behavior.peekHeight = displayMetrics.heightPixels
} else {
(dialog as BottomSheetDialog).behavior.state = state
}
}
override fun getTheme(): Int {
return R.style.CustomBottomSheetDialog
}
}
CustomBottomSheetDialog Style
<style name="CustomBottomSheetDialog" parent="#style/ThemeOverlay.MaterialComponents.BottomSheetDialog">
<item name="bottomSheetStyle">#style/CustomBottomSheet</item>
<item name="materialButtonStyle">#style/CustomMaterialButtonStyle</item>
</style>
<style name="CustomMaterialButtonStyle" parent="#style/Widget.MaterialComponents.Button">
<item name="cornerRadius">#dimen/dialog_bottom_radius</item>
</style>
<style name="CustomBottomSheet" parent="Widget.MaterialComponents.BottomSheet">
<item name="shapeAppearanceOverlay">#style/CustomShapeAppearanceBottomSheetDialog</item>
</style>
<style name="CustomShapeAppearanceBottomSheetDialog" parent="">
<item name="android:background">#android:color/transparent</item>
<item name="backgroundTint">#android:color/transparent</item>
<item name="cornerFamily">rounded</item>
<item name="cornerSizeTopRight">#dimen/dialog_bottom_radius</item>
<item name="cornerSizeTopLeft">#dimen/dialog_bottom_radius</item>
<item name="cornerSizeBottomRight">0dp</item>
<item name="cornerSizeBottomLeft">0dp</item>
</style>
Shorter solution to disable flatting of rounded corners on STATE_EXPANDED:
#SuppressLint("RestrictedApi")
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
//Disable animator that flats the rounded corners
(dialog as BottomSheetDialog).behavior.disableShapeAnimations()
return dialog
}
Add these two methods in your BottomsheetDialogFragment class.
public void setDialogBorder(Dialog dialog) {
FrameLayout bottomSheet = (FrameLayout) dialog.getWindow().findViewById(android.support.design.R.id.design_bottom_sheet);
bottomSheet.setBackground(new ColorDrawable(Color.TRANSPARENT));
setMargins(bottomSheet, 10, 0, 10, 20);
}
private void setMargins(View view, int left, int top, int right, int bottom) {
if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
p.setMargins(left, top, right, bottom);
view.requestLayout();
}
}
Now call setDialogBorder(dialog) method in setupDialog() method of your BottomsheetDialogFragment class.
Now create a shape file in your drawable folder.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="20dp" />
<solid android:color="#color/white" />
<stroke
android:width="1dp"
android:color="#color/transparent" />
</shape>
Now set background for the parent viewgroup dialog view in xml file.
android:background="#drawable/round_border_white"
Done!!
You have to change the bottom sheet theme to achieve the top round layout
Create a custom drawable background_bottom_sheet_dialog_fragment.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="8dp"
android:topRightRadius="8dp" />
<padding android:top="0dp" />
<solid android:color="#color/white" />
</shape>
Then override bottomSheetDialogTheme on styles.xml using the drawable as background:
<!--Bottom sheet-->
<style name="BottomSheet" parent="#style/Widget.Design.BottomSheet.Modal">
<item
name="android:background">#drawable/background_bottom_sheet_dialog_fragment
</item>
</style>
<style name="BaseBottomSheetDialog"
parent="#style/Theme.Design.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="bottomSheetStyle">#style/BottomSheet</item>
</style>
<style name="BottomSheetDialogTheme" parent="BaseBottomSheetDialog" />
This will change the background layout of your bottom sheet
BottomSheetDialog
class SheetFragment() : BottomSheetDialogFragment() {
lateinit var binding: SheetFragmentBinding;
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog;
val view = View.inflate(context, R.layout.fragment_bottom_sheet, null);
binding = DataBindingUtil.bind(view)!!;
binding.viewModel = SheetFragmentVM();
dialog.setContentView(view);
var bottomSheetBehavior = BottomSheetBehavior.from(view.parent as View);
bottomSheetBehavior.setPeekHeight(BottomSheetBehavior.PEEK_HEIGHT_AUTO);
bottomSheetBehavior.setBottomSheetCallback(object :
BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (BottomSheetBehavior.STATE_EXPANDED == newState) {
// do on STATE_EXPANDED
}
if (BottomSheetBehavior.STATE_COLLAPSED == newState) {
// do on STATE_COLLAPSED
}
if (BottomSheetBehavior.STATE_HIDDEN == newState) {
dismiss()
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
// do on slide
}
})
return dialog
}
add shape with rounded corner, make it background for your root layout
<?xml version="1.0" encoding="utf-8" ?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="#dimen/padding_margin_16_dp"
android:topRightRadius="#dimen/padding_margin_16_dp" />
<solid android:color="#color/white" />
</shape>
make background transparent on your BottomSheetDialogFragment
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
(view?.parent as View).setBackgroundColor(Color.TRANSPARENT)
}
its work for Contraintlayout, Framelyaout, Linearlayout, Relativelayout.
Complete Solution:
Add below style attribute to style.xml.
<style name="AppBottomSheetDialogTheme"
parent="Theme.Design.Light.BottomSheetDialog">
<item name="bottomSheetStyle">#style/AppModalStyle</item>
</style>
<style name="AppModalStyle"
parent="Widget.Design.BottomSheet.Modal">
<item name="android:background">#drawable/bottom_sheet_background</item>
</style>
Then use AppBottomSheetDialogTheme to create a bottom sheet dialog from your code.
private fun openBottomSheetTermsCondition() {
val mBottomSheetDialog = BottomSheetDialog(requireContext(),R.style.AppBottomSheetDialogTheme)
val sheetView = layoutInflater.inflate(R.layout.bottom_sheet_travel_advice_terms, null)
mBottomSheetDialog.setContentView(sheetView)
sheetView.tv_head.setOnClickListener {
mBottomSheetDialog.dismiss()
}
sheetView.webView.loadDataWithBaseURL(null,getString(R.string.privacy_policy_body_html),"text/html", "utf-8", null)
mBottomSheetDialog.show()
}
I use the below drawable to round the bottom sheet background.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="#dimen/bottom_sheet_corner_radius"
android:topRightRadius="#dimen/bottom_sheet_corner_radius" />
<solid android:color="#color/white" />
</shape>
Bottom sheet xml bottom_sheet_travel_advice_terms.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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="match_parent"
app:behavior_hideable="false"
app:behavior_peekHeight="#dimen/bottom_sheet_peek_height"
app:cardCornerRadius="#dimen/spacing_normal"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/spacing_small">
<androidx.constraintlayout.widget.Guideline
android:id="#+id/begin_horizontal_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="#dimen/activity_vertical_margin" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/begin_vertical_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="#dimen/activity_horizontal_margin" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/end_vertical_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_end="#dimen/activity_horizontal_margin" />
<View
android:id="#+id/sheet_header_shadow"
android:layout_width="match_parent"
android:layout_height="#dimen/spacing_tiny"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:background="#drawable/bottom_sheet_header_shadow"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_head"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:drawablePadding="#dimen/spacing_normal"
android:fontFamily="sans-serif-medium"
android:gravity="start"
android:padding="#dimen/spacing_small"
android:text="#string/term_and_condition"
android:textColor="#color/greyish_brown"
android:textSize="20sp"
app:drawableLeftCompat="#drawable/ic_close_black_24dp"
app:layout_constraintEnd_toEndOf="#id/end_vertical_guideline"
app:layout_constraintStart_toStartOf="#id/begin_vertical_guideline"
app:layout_constraintTop_toBottomOf="#+id/begin_horizontal_guideline" />
<View
android:id="#+id/line_separation"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="#dimen/spacing_small"
android:background="#color/blue_gray"
app:layout_constraintTop_toBottomOf="#+id/tv_head" />
<WebView
android:id="#+id/webView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="#id/end_vertical_guideline"
app:layout_constraintStart_toStartOf="#id/begin_vertical_guideline"
app:layout_constraintTop_toBottomOf="#id/line_separation" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
setupDialog() is RestrictedApi. The simplest solution that works as of material:1.3.0-beta01 without touching themes:
res/drawable/bs_background:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="16dp"
android:topRightRadius="16dp" />
<solid android:color="#color/dayNightBackground" />
</shape>
public class MyBsDialogFrag extends BottomSheetDialogFragment {
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
((View) view.getParent()).setBackgroundResource(R.drawable.bs_background);
}
}
firstly you should create a drawable xml file that contains a shape with a top rounded corner, name whatever you want.
I named it a bottom rounded_top_shape.xml
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<solid android:color="#android:color/white" />
<corners
android:topLeftRadius="16dp"
android:topRightRadius="16dp"
/>
then in your style.xml add this
<style name="AppBottomSheetDialogTheme" parent="Theme.MaterialComponents.Light.BottomSheetDialog">
<item name="bottomSheetStyle">#style/AppModalStyle</item>
</style>
<style name="AppModalStyle" parent="Widget.Design.BottomSheet.Modal">
<item name="android:background">#drawable/rounded_top_shape</item>
</style>
then in your app theme add this line as bellow
<style name="MyAppTheme" parent="Theme.MaterialComponents.Light.Bridge">
<!-- this line -->
<item name="bottomSheetDialogTheme">#style/AppBottomSheetDialogTheme</item>
</style>
I found simple solution.
works with com.google.android.material:material:1.6.1
class MyBottomSheet: BottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
disableShapeAnimation()
}
#SuppressLint("RestrictedApi", "VisibleForTests")
private fun disableShapeAnimation() {
try {
val dlg = dialog as BottomSheetDialog
dlg.behavior.disableShapeAnimations()
} catch (ex: Exception) {
Log.e("BaseBottomSheet", "disableShapeAnimation Exception:", ex)
}
}
}
Another way to fix this issue is to extend BottomSheetDialog and create a custom class which suits your needs. You can do the same for layout xml file, and add background or any other customizations needed. This also has a benefit that you'll not be dependent on the id names used by Android(android.support.design.R.id.design_bottom_sheet), while changing the background (though the change of id name rarely happens AFAIK).
Create a custom drawable with rounded corner and set it as background of your BottomSheetDialogFragment's layout root
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#color/colorPrimary" />
<corners
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"
android:topLeftRadius="12dp"
android:topRightRadius="12dp" />
</shape>
And then simply add the below code to your BottomSheetDialogFragment class
#Override
public void setupDialog(Dialog dialog, int style) {
super.setupDialog(dialog, style);
View contentView = View.inflate(getContext(),
R.layout.fragment_bottom_sheet, null);
dialog.setContentView(contentView);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) ((View) contentView.getParent())
.getLayoutParams();
CoordinatorLayout.Behavior behavior = params.getBehavior();
((View) contentView.getParent()).setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
}
You can even play with the params to set margin like below
params.setMargins(50, 0, 50, 0);
If you need setFitContents=true, I tried the solution by hooking onStateChanged, but it flickers from the straight to rounded corners once the dialog reaches EXPANDED state. It's quite annoying.
There is an alternative workaround that doesn't cause flickering, doesn't require using private APIs, and is more readable (imho).
Looking at the code of BottomSheetBehavior we find:
/** True if Behavior has a non-null value for the #shapeAppearance attribute */
private boolean shapeThemingEnabled;
Turns out if shape theming is disabled, MaterialShapeDrawable will not be used. We find this in BottomSheetBehavior.onLayout():
// Only set MaterialShapeDrawable as background if shapeTheming is enabled, otherwise will
// default to android:background declared in styles or layout.
if (shapeThemingEnabled && materialShapeDrawable != null) {
ViewCompat.setBackground(child, materialShapeDrawable);
}
Defaulting to android:background is exactly what we need, as this means complete control on how the background is rendered.
We can disable material theming by creating a separate style and setting shapeAppearance and shapeAppearanceOverlay to null:
<style name="Theme.YourApp.NoShapeBottomSheetDialog" parent="Theme.MaterialComponents.BottomSheetDialog">
<item name="bottomSheetStyle">#style/Theme.YourApp.NoShapeButtonSheet</item>
</style>
<style name="Theme.YourApp.NoShapeButtonSheet" parent="Widget.MaterialComponents.BottomSheet.Modal">
<item name="shapeAppearance">#null</item>
<item name="shapeAppearanceOverlay">#null</item>
</style>
Extend BottomSheetDialogFragment and override onCreateDialog:
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
return new BottomSheetDialog(requireContext(),
R.style.Theme_Grupin_NoShapeBottomSheetDialog);
}
The bottom sheet is now naked, without any background at all. So we can add any background we want, no animation will be triggered anymore.
If you are using material components theme, you can override the default behavoir of BottomSheetDialog by setting the background color to transparent then set the background of your layout to be rounded.
<style name="Theme.MyApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Customize your theme here. -->
<item name="bottomSheetDialogTheme">#style/BottomSheetDialog</item>
</style>
<style name="BottomSheetDialog" parent="ThemeOverlay.MaterialComponents.BottomSheetDialog">
<item name="bottomSheetStyle">#style/bottomSheetStyleWrapper</item>
</style>
<style name="bottomSheetStyleWrapper" parent="Widget.MaterialComponents.BottomSheet.Modal">
<item name="android:backgroundTint">#android:color/transparent</item>
</style>
rounded_corner.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#color/white"/>
<corners android:topLeftRadius="26dp"
android:topRightRadius="26dp"/>
</shape>
Then apply the shape to background of your bottom sheet root layout:
<?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:background="#drawable/rounded_corner">
<!--
other layout components
-->
</LinearLayout>
Note that if you're having component like Webview in the bottom sheet layout, you may not be able to achieve the rounded corner as expected because Webview has a default white background. In this case, you can simply remove the background like this:
webView.setBackgroundColor(0);
What I am trying to do: I am trying to make a custom dialog in android With rounded corners.
What is happening: I am able to make custom dialog but it doesn't have rounded corners. I tried adding a selector but still I couldn't achieve rounded corners.
Below is my code for the same:
Java code:
private void launchDismissDlg() {
dialog = new Dialog(getActivity(), android.R.style.Theme_Dialog);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(R.layout.dlg_dismiss);
dialog.setCanceledOnTouchOutside(true);
Button btnReopenId = (Button) dialog.findViewById(R.id.btnReopenId);
Button btnCancelId = (Button) dialog.findViewById(R.id.btnCancelId);
btnReopenId.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
}
});
btnCancelId.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
}
});
dialog.setCanceledOnTouchOutside(false);
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
dialog.getWindow().setLayout(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT);
dialog.show();
}
xml code:
<?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:background="#android:color/white"
android:orientation="vertical" >
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TableRow
android:id="#+id/tableRow1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text=""I WOULD LIKE TO DISMISS THE VENDOR""
android:textColor="#color/col_dlg_blue_light"
android:textSize="14sp"
android:textStyle="bold" />
</TableRow>
<TableRow
android:id="#+id/tableRow2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:gravity="center" >
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="BECAUSE"
android:textColor="#android:color/black"
android:textStyle="bold" />
</TableRow>
<TableRow
android:id="#+id/tableRow4"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="#+id/btnReopenId"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#color/col_dlg_green_light"
android:text="REOPEN"
android:padding="5dp"
android:textSize="14sp"
android:textColor="#android:color/white"
android:textStyle="bold" />
<Button
android:id="#+id/btnCancelId"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#color/col_dlg_pink_light"
android:text="CANCEL"
android:padding="5dp"
android:textSize="14sp"
android:textColor="#android:color/white"
android:textStyle="bold" />
</TableRow>
</TableLayout>
</LinearLayout>
Create an XML file in drawable, say dialog_bg.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid
android:color="#color/white"/>
<corners
android:radius="30dp" />
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" />
</shape>
set it as the background in your layout XML:
android:background="#drawable/dialog_bg"
Set the background of the dialog's root view to transparent, because Android puts your dialog layout within a root view that hides the corners in your custom layout.
Java:
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
Kotlin:
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
With the Androidx library and Material Components Theme you can override the getTheme() method:
import androidx.fragment.app.DialogFragment
class RoundedDialog: DialogFragment() {
override fun getTheme() = R.style.RoundedCornersDialog
//....
}
with:
<style name="RoundedCornersDialog" parent="#style/Theme.MaterialComponents.Dialog">
<item name="dialogCornerRadius">16dp</item>
</style>
Or you can use the MaterialAlertDialogBuilder included in the Material Components Library:
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
class RoundedAlertDialog : DialogFragment() {
//...
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return MaterialAlertDialogBuilder(requireActivity(), R.style.MaterialAlertDialog_rounded)
.setTitle("Test")
.setMessage("Message")
.setPositiveButton("OK", null)
.create()
}
}
with:
<style name="MaterialAlertDialog_rounded" parent="#style/ThemeOverlay.MaterialComponents.MaterialAlertDialog">
<item name="shapeAppearanceOverlay">#style/DialogCorners</item>
</style>
<style name="DialogCorners">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">16dp</item>
</style>
If you don't need a DialogFragment just use the MaterialAlertDialogBuilder.
You need to do the following:
Create a background with rounded corners for the Dialog's background:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#fff" />
<corners
android:bottomLeftRadius="8dp"
android:bottomRightRadius="8dp"
android:topLeftRadius="8dp"
android:topRightRadius="8dp" />
</shape>
Now in your Dialog's XML file in the root layout use that background with required margin:
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:background="#drawable/dialog_background"
finally in the java part you need to do this:
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(layoutResId);
View v = getWindow().getDecorView();
v.setBackgroundResource(android.R.color.transparent);
This works perfectly for me.
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
this works for me
Setting
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
will prevent dialog to cast a shadow.
Solution is to use
dialog.getWindow().setBackgroundDrawableResource(R.drawable.dialog_rounded_background);
where is R.drawable.dialog_rounded_background
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape android:shape="rectangle" android:padding="10dp">
<solid
android:color="#color/dialog_bg_color"/>
<corners
android:radius="30dp" />
</shape>
</item>
</layer-list>
If you use Material Components:
CustomDialog.kt
class CustomDialog: DialogFragment() {
override fun getTheme() = R.style.RoundedCornersDialog
}
styles.xml
<style name="RoundedCornersDialog" parent="Theme.MaterialComponents.Dialog">
<item name="dialogCornerRadius">dimen</item>
</style>
dimen.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="weight">1</integer>
<dimen name="dialog_top_radius">21dp</dimen>
<dimen name="textview_dialog_head_min_height">50dp</dimen>
<dimen name="textview_dialog_drawable_padding">5dp</dimen>
<dimen name="button_dialog_layout_margin">3dp</dimen>
</resources>
styles.xml
<style name="TextView.Dialog">
<item name="android:paddingLeft">#dimen/dimen_size</item>
<item name="android:paddingRight">#dimen/dimen_size</item>
<item name="android:gravity">center_vertical</item>
<item name="android:textColor">#color/black</item>
</style>
<style name="TextView.Dialog.Head">
<item name="android:minHeight">#dimen/textview_dialog_head_min_height</item>
<item name="android:textColor">#color/white</item>
<item name="android:background">#drawable/dialog_title_style</item>
<item name="android:drawablePadding">#dimen/textview_dialog_drawable_padding</item>
</style>
<style name="TextView.Dialog.Text">
<item name="android:textAppearance">#style/Font.Medium.16</item>
</style>
<style name="Button" parent="Base.Widget.AppCompat.Button">
<item name="android:layout_height">#dimen/button_min_height</item>
<item name="android:layout_width">match_parent</item>
<item name="android:textColor">#color/white</item>
<item name="android:gravity">center</item>
<item name="android:textAppearance">#style/Font.Medium.20</item>
</style>
<style name="Button.Dialog">
<item name="android:layout_weight">#integer/weight</item>
<item name="android:layout_margin">#dimen/button_dialog_layout_margin</item>
</style>
<style name="Button.Dialog.Middle">
<item name="android:background">#drawable/button_primary_selector</item>
</style>
dialog_title_style.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="270"
android:endColor="#color/primaryDark"
android:startColor="#color/primaryDark" />
<corners
android:topLeftRadius="#dimen/dialog_top_radius"
android:topRightRadius="#dimen/dialog_top_radius" />
</shape>
dialog_background.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#color/backgroundDialog" />
<corners
android:topLeftRadius="#dimen/dialog_top_radius"
android:topRightRadius="#dimen/dialog_top_radius" />
<padding />
</shape>
dialog_one_button.xml
<?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="wrap_content"
android:background="#drawable/dailog_background"
android:orientation="vertical">
<TextView
android:id="#+id/dialogOneButtonTitle"
style="#style/TextView.Dialog.Head"
android:text="Process Completed" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:orientation="vertical">
<TextView
android:id="#+id/dialogOneButtonText"
style="#style/TextView.Dialog.Text"
android:text="Return the main menu" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="#+id/dialogOneButtonOkButton"
style="#style/Button.Dialog.Middle"
android:text="Ok" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
OneButtonDialog.java
package com.example.sametoztoprak.concept.dialogs;
import android.app.Dialog;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import com.example.sametoztoprak.concept.R;
import com.example.sametoztoprak.concept.models.DialogFields;
/**
* Created by sametoztoprak on 26/09/2017.
*/
public class OneButtonDialog extends Dialog implements View.OnClickListener {
private static OneButtonDialog oneButtonDialog;
private static DialogFields dialogFields;
private Button dialogOneButtonOkButton;
private TextView dialogOneButtonText;
private TextView dialogOneButtonTitle;
public OneButtonDialog(AppCompatActivity activity) {
super(activity);
}
public static OneButtonDialog getInstance(AppCompatActivity activity, DialogFields dialogFields) {
OneButtonDialog.dialogFields = dialogFields;
return oneButtonDialog = (oneButtonDialog == null) ? new OneButtonDialog(activity) : oneButtonDialog;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.dialog_one_button);
getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialogOneButtonTitle = (TextView) findViewById(R.id.dialogOneButtonTitle);
dialogOneButtonText = (TextView) findViewById(R.id.dialogOneButtonText);
dialogOneButtonOkButton = (Button) findViewById(R.id.dialogOneButtonOkButton);
dialogOneButtonOkButton.setOnClickListener(this);
}
#Override
protected void onStart() {
super.onStart();
dialogOneButtonTitle.setText(dialogFields.getTitle());
dialogOneButtonText.setText(dialogFields.getText());
dialogOneButtonOkButton.setText(dialogFields.getOneButton());
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.dialogOneButtonOkButton:
break;
default:
break;
}
dismiss();
}
}
I made a new way without having a background drawable is that make it have CardView as parent and give it a app:cardCornerRadius="20dp" and then add this in the java class dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
It's another way to make it .
You can simply use MaterialAlertDialogBuilder to create custom dialog with rounded corners.
First create a style for the material dialog like this :
<style name="MyRounded.MaterialComponents.MaterialAlertDialog" parent="#style/ThemeOverlay.MaterialComponents.MaterialAlertDialog">
<item name="shapeAppearanceOverlay">#style/ShapeAppearanceOverlay.App.CustomDialog.Rounded
</item>
<item name="colorSurface">#color/YOUR_COLOR</item>
</style>
<style name="ShapeAppearanceOverlay.App.CustomDialog.Rounded" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">10dp</item>
</style>
then create a Alert Dialog object in Java class like this :
AlertDialog alertDialog = new MaterialAlertDialogBuilder(this,R.style.MyRounded_MaterialComponents_MaterialAlertDialog) // for fragment you can use getActivity() instead of this
.setView(R.layout.custom_layout) // custom layout is here
.show();
final EditText editText = alertDialog.findViewById(R.id.custom_layout_text); // access to text view of custom layout
Button btn = alertDialog.findViewById(R.id.custom_layout_btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "onClick: " + editText.getText().toString());
}
});
That's all you need to do.
For anyone who like do things in XML, specially in case where you are using Navigation architecture component actions in order to navigate to dialogs
You can use:
<style name="DialogStyle" parent="ThemeOverlay.MaterialComponents.Dialog.Alert">
<!-- dialog_background is drawable shape with corner radius -->
<item name="android:background">#drawable/dialog_background</item>
<item name="android:windowBackground">#android:color/transparent</item>
</style>
simplest way is to use from
CardView and its card:cardCornerRadius
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:id="#+id/cardlist_item"
android:layout_width="match_parent"
android:layout_height="130dp"
card:cardCornerRadius="40dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:background="#color/white">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="12sp"
android:orientation="vertical"
android:weightSum="1">
</RelativeLayout>
</android.support.v7.widget.CardView>
And when you are creating your Dialog
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
You can use the shape for the background as-
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#android:color/transparent"/>
<corners android:radius="10dp" />
<padding android:left="10dp" android:right="10dp"/>
</shape>
Have a look on this for the details.
Here is a Basic Solution:
<style name="Style_Dialog_Rounded_Corner" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="android:windowBackground">#drawable/dialog_rounded_corner</item>
<item name="android:windowMinWidthMinor">85%</item>
</style>
in Drawable create shape:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF" />
<corners android:radius="12dp" />
</shape>
Pass style in Builder Constructor
AlertDialog alert = new AlertDialog.Builder(MainActivity.this,R.style.Style_Dialog_Rounded_Corner).create();
Here is the complete solution if you want to control the corner radius of the dialog and preserve elevation shadow
Dialog:
class OptionsDialog: DialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View {
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
return inflater.inflate(R.layout.dialog_options, container)
}
}
dialog_options.xml layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="40dp"
app:cardElevation="20dp"
app:cardCornerRadius="12dp">
<androidx.constraintlayout.widget.ConstraintLayout
id="#+id/actual_content_goes_here"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</FrameLayout>
The key is to wrap the CardView with another ViewGroup (here FrameLayout) and set margins to create space for the elevation shadow.
For API level >= 28 available attribute android:dialogCornerRadius . To support previous API versions need use
<style name="RoundedDialog" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="android:windowBackground">#drawable/dialog_bg</item>
</style>
where dialog_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item >
<shape >
<solid android:color="#android:color/transparent" />
</shape>
</item>
<item
android:left="16dp"
android:right="16dp">
<shape>
<solid
android:color="#color/white"/>
<corners
android:radius="8dp" />
<padding
android:left="16dp"
android:right="16dp" />
</shape>
</item>
</layer-list>
In Kotlin, I am using a class DoubleButtonDialog.Java with line window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) as important one
class DoubleButtonDialog(context: Context) : Dialog(context, R.style.DialogTheme) {
private var cancelableDialog: Boolean = true
private var titleDialog: String? = null
private var messageDialog: String? = null
private var leftButtonDialog: String = "Yes"
// private var rightButtonDialog: String? = null
private var onClickListenerDialog: OnClickListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
//requestWindowFeature(android.view.Window.FEATURE_NO_TITLE)
setCancelable(cancelableDialog)
setContentView(R.layout.dialog_double_button)
// val btnNegative = findViewById<Button>(R.id.btnNegative)
// btnNegative.visibility = View.GONE
// if (rightButtonDialog != null) {
// btnNegative.visibility = View.VISIBLE
// btnNegative.text = rightButtonDialog
// btnNegative.setOnClickListener {
// dismiss()
// onClickListenerDialog?.onClickCancel()
// }
// }
val btnPositive = findViewById<Button>(R.id.btnPositive)
btnPositive.text = leftButtonDialog
btnPositive.setOnClickListener {
onClickListenerDialog?.onClick()
dismiss()
}
(findViewById<TextView>(R.id.title)).text = titleDialog
(findViewById<TextView>(R.id.message)).text = messageDialog
super.onCreate(savedInstanceState)
}
constructor(
context: Context, cancelableDialog: Boolean, titleDialog: String?,
messageDialog: String, leftButtonDialog: String, /*rightButtonDialog: String?,*/
onClickListenerDialog: OnClickListener
) : this(context) {
this.cancelableDialog = cancelableDialog
this.titleDialog = titleDialog
this.messageDialog = messageDialog
this.leftButtonDialog = leftButtonDialog
// this.rightButtonDialog = rightButtonDialog
this.onClickListenerDialog = onClickListenerDialog
}
}
interface OnClickListener {
// fun onClickCancel()
fun onClick()
}
In layout, we can create a dialog_double_button.xml
<?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:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="#dimen/dimen_10"
android:background="#drawable/bg_double_button"
android:orientation="vertical"
android:padding="#dimen/dimen_5">
<TextView
android:id="#+id/title"
style="#style/TextViewStyle"
android:layout_gravity="center_horizontal"
android:layout_margin="#dimen/dimen_10"
android:fontFamily="#font/campton_semi_bold"
android:textColor="#color/red_dark4"
android:textSize="#dimen/text_size_24"
tools:text="#string/dial" />
<TextView
android:id="#+id/message"
style="#style/TextViewStyle"
android:layout_gravity="center_horizontal"
android:layout_margin="#dimen/dimen_10"
android:gravity="center"
android:textColor="#color/semi_gray_2"
tools:text="#string/diling_police_number" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/dimen_10"
android:gravity="center"
android:orientation="horizontal"
android:padding="#dimen/dimen_5">
<!--<Button
android:id="#+id/btnNegative"
style="#style/ButtonStyle"
android:layout_width="0dp"
android:layout_height="#dimen/dimen_40"
android:layout_marginEnd="#dimen/dimen_10"
android:layout_weight=".4"
android:text="#string/cancel" />-->
<Button
android:id="#+id/btnPositive"
style="#style/ButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="#color/red_dark4"
android:fontFamily="#font/campton_semi_bold"
android:padding="#dimen/dimen_10"
android:text="#string/proceed"
android:textAllCaps="false"
android:textColor="#color/white"
android:textSize="#dimen/text_size_20" />
</LinearLayout>
</LinearLayout>
then use drawable.xml as
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid
android:color="#color/white"/>
<corners
android:radius="#dimen/dimen_10" />
<padding
android:left="#dimen/dimen_10"
android:top="#dimen/dimen_10"
android:right="#dimen/dimen_10"
android:bottom="#dimen/dimen_10" />
</shape>
Create a xml in drawable ,say customd.xml.
then set it at background in your custom Dialog layout xml:
android:background="#drawable/customd"
finally in the java part for custom Dialog class you need to do this:
public class Customdialoque extends DialogFragment {
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
View view = inflater.inflate(R.layout.activity_customdialoque, container, false);
return view;
}
I implemented rounded dialog using CardView in the custom layout and setting its corner radius.
Here's my xml code.
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/bottomSheet"
android:layout_width="match_parent"
android:layout_margin="#dimen/padding_5dp"
app:cardCornerRadius="#dimen/dimen_20dp"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/main_gradient_bg"
android:paddingBottom="32dp">
<TextView
android:id="#+id/subdomain_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/margin_32dp"
android:layout_marginLeft="#dimen/margin_32dp"
android:layout_marginTop="#dimen/margin_50dp"
android:fontFamily="#font/nunito_sans"
android:text="#string/enter_subdomain"
android:textColor="#color/white"
android:textSize="#dimen/size_18sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/dimen_45dp"
app:layout_constraintLeft_toRightOf="#id/subdomain_label"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/ic_baseline_info_24" />
<EditText
android:id="#+id/subdomain_edit_text_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/dimen_20dp"
android:layout_marginLeft="#dimen/dimen_20dp"
android:layout_marginEnd="#dimen/dimen_20dp"
android:layout_marginRight="#dimen/dimen_20dp"
android:textColor="#color/white"
android:theme="#style/EditTextTheme"
app:layout_constraintTop_toBottomOf="#id/subdomain_label" />
<Button
android:id="#+id/proceed_btn"
android:layout_width="#dimen/dimen_150dp"
android:layout_height="#dimen/margin_50dp"
android:layout_marginTop="#dimen/margin_30dp"
android:background="#drawable/primary_btn_bg"
android:text="#string/proceed"
android:textAllCaps="false"
android:textColor="#color/white"
android:textSize="#dimen/size_18sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#id/subdomain_edit_text_bottom_sheet" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
After that, i called it in Kotlin as follows :-
val builder = AlertDialog.Builder(mContext)
val viewGroup: ViewGroup = findViewById(android.R.id.content)
val dialogView: View =
LayoutInflater.from(mContext).inflate(R.layout.subdomain_bottom_sheet,
viewGroup, false)
val alertDialog: AlertDialog = builder.create()
alertDialog.setView(dialogView,0,0,0,0)
alertDialog.show()
val windowParam = WindowManager.LayoutParams()
windowParam.copyFrom(alertDialog.window!!.attributes)
windowParam.width = AppConstant.getDisplayMetricsWidth(mContext) - 100
windowParam.height = WindowManager.LayoutParams.WRAP_CONTENT
windowParam.gravity = Gravity.CENTER
alertDialog.window!!.attributes = windowParam
alertDialog.window!!.setBackgroundDrawable
(ColorDrawable(Color.TRANSPARENT))
Where last line is very important. Missing that would cause a color(mostly white) to show behind the corners.
The accepted answer did round the corners for me, but unless I switched to a Material design theme, making the window background transparent had no effect and I still saw a square, shadowed background of the dialog inside my rounded background. And I don't want to switch to a Material theme because that changes the functionality of date and time pickers.
Instead, I had to hide the backgrounds of several subviews of the dialog in order to see the rounded background. And after doing all that, it was easier just to add the rounded background in code, too. So here's a fully programmatic (no XML) solution. This is in the onStart method of my DialogFragment subclass:
// add rounded corners
val backgroundShape = GradientDrawable()
backgroundShape.cornerRadius = 10.0f
backgroundShape.setColor(Color.BLUE)
this.dialog.window?.decorView?.background = backgroundShape
// make the backgrounds of the dialog elements transparent so we can see the rounded corner background
val topPanelId = this.context.resources.getIdentifier("topPanel", "id", "android")
val topPanel = this.dialog.findViewById<View>(topPanelId)
topPanel?.setBackgroundColor(Color.TRANSPARENT)
val contentPanelId = this.context.resources.getIdentifier("contentPanel", "id", "android")
val contentPanel = this.dialog.findViewById<View>(contentPanelId)
contentPanel?.setBackgroundColor(Color.TRANSPARENT)
val customPanelId = this.context.resources.getIdentifier("customPanel", "id", "android")
val customPanel = this.dialog.findViewById<View>(customPanelId)
customPanel?.setBackgroundColor(Color.TRANSPARENT)
val buttonPanelId = this.context.resources.getIdentifier("buttonPanel", "id", "android")
val buttonPanel = this.dialog.findViewById<View>(buttonPanelId)
buttonPanel?.setBackgroundColor(Color.TRANSPARENT)
Here is another way in kotlin:
val model = ShapeAppearanceModel()
.toBuilder()
.setAllCorners(CornerFamily.ROUNDED, 32.0f)
.build()
val shape = MaterialShapeDrawable(model)
shape.fillColor = ContextCompat.getColorStateList(context, R.color.white)
ViewCompat.setBackground(dialogContentView, shape)
And then change the dialog window
dialog?.window?.setBackgroundDrawableResource(android.R.color.transparent)
I'm trying to figure out how to add an outer glow to a TextView when it's touched. The approach I'm working with is to use a Selector, but it doesn't seem to be working.
I've got the following structure
layout\HomeView.axml
<TextView
android:id="#+id/textview1"
android:clickable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
style="#drawable/control_selector_state" />
drawable\control_selector_state.xml
<!-- yes these are all the same for testing purposes -->
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true"
style="#style/control_style_focused"/>
<item android:state_focused="true"
android:state_pressed="true"
style="#style/control_style_focused" />
<item android:state_pressed="true"
style="#style/control_style_focused" />
<item style="#style/control_style_focused" />
</selector>
values\styles.xml
<resources>
<style name="control_style_focused">
<item name="android:shadowColor">#0000ff</item>
<item name="android:textColor">#ff0000</item>
<item name="android:shadowDx">0.0</item>
<item name="android:shadowRadius">8</item>
</style>
</resources>
The problem I'm having is that my TextView text is white, and the style doesn't seem to be applying.
How do I get this style to apply to my TextView?
so as #Longwayto said, selector styles are only available for drawables. That doesn't mean it's impossible.
Here's a working approach.
First you create a custom TextView that extends TextView
public class MyTextView: TextView
{
private readonly Context _context;
public FontIconTextView(Context context, IAttributeSet attrs) : base(context)
{
_context = context;
Initialize(attrs);
}
private void Initialize(IAttributeSet attrs)
{
var a = _context.Theme.ObtainStyledAttributes(attrs, Resource.Styleable.MyTextView, 0, 0);
_touchGlowColor = a.GetString(Resource.Styleable.MyTextView_TouchGlowColor);
_touchGlowSize = a.GetInteger(Resource.Styleable.MyTextView_TouchGlowSize, 0);
Initialize();
}
private void Initialize()
{
// other initialize stuff happens here...
}
private int _touchGlowSize;
private string _touchGlowColor;
public override bool OnTouchEvent(MotionEvent motionEvent)
{
if (Enabled)
{
var color = string.IsNullOrEmpty(_touchGlowColor) ? new Color(255, 255, 255, 255) : Color.ParseColor(_touchGlowColor);
switch (motionEvent.Action)
{
case MotionEventActions.Down:
SetShadowLayer(_touchGlowSize, 0, 0, color);
break;
case MotionEventActions.Up:
case MotionEventActions.Cancel:
SetShadowLayer(0, 0, 0, new Color(255, 255, 255, 255));
break;
}
}
return base.OnTouchEvent(motionEvent);
}
}
then, you have to go into your values directory and specify your custom attributes.
Resources\values\CustomBindingAttributes.xml
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<declare-styleable name="MyTextView">
<attr name="TouchGlowColor" format="string" />
<attr name="TouchGlowSize" format="integer" />
</declare-styleable>
</resources>
All of the above will be reusable across your entire application... no more duplicating shit on every view.
Lastly, here's how your button will look.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- obviously the foo/bar is just to show that you add android attrs like normal -->
<some.name.space.MyTextView
android:foo="foo"
amdroid:bar="bar"
local:TouchGlowColor="#66e400"
local:TouchGlowSize="20" />
</LinearLayout>
one gotcha I ran into is setting TouchGlowSize to 30 caused the app to crash. not sure why atm