Action mode close button text color - android

I see to set the text color of the action mode "done" / "close" button. This is what I've tried:
<item name="android:actionModeCloseButtonStyle">#style/ActionModeCloseButton</item>
....
<style name="ActionModeCloseButton" parent="android:style/Widget.Holo.ActionButton.CloseMode">
<item name="android:textColor">#android:color/white</item>
</style>
but is has no effect.
Note that on JB, it's enough that I make the ActionModeCloseButton style's parent the regular holo theme. It works fine there (without the textColor setting even).
Any ideas?

First of all, the textview "Done" is only visible on large devices.
Checkout action_mode_close_item.xml in the Android source.
So the android:actionModeCloseButtonStyle only applies to the containing view and not the imageview and the textview.
Luckily, the android engineers used publicly accessible attributes to styles the childviews.
Use android:actionMenuTextColor to change to textColor of the TextView.
Use android:actionModeCloseDrawable to change the drawable of the ImageView
Example:
<style name="MyTheme">
<item name="android:actionMenuTextColor">#ff000000</item>
<item name="android:actionModeCloseDrawable">#drawable/my_close_drawable</item>
</style>
Below is a copy of the action_mode_close_item.xml in the layout-large-folder where you can see how the layout is build.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/action_mode_close_button"
android:focusable="true"
android:clickable="true"
android:paddingStart="8dip"
style="?android:attr/actionModeCloseButtonStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="16dip">
<ImageView android:layout_width="48dip"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="center"
android:src="?android:attr/actionModeCloseDrawable" />
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="4dip"
android:layout_marginEnd="16dip"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/actionMenuTextColor"
android:textSize="12sp"
android:textAllCaps="true"
android:text="#string/action_mode_done" />
</LinearLayout>

Since the layout for the action mode close button doesn't provide a color attribute for the text view, there is no way to set this color in a custom theme. Instead the only ay I found was to overwrite the text color in the onPrepareActionMode() method of my derived ActionMode class:
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... none) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
return null;
}
#Override
protected void onPostExecute(Void none) {
if (activity != null) {
LinearLayout layout = (LinearLayout) activity
.findViewById(R.id.abs__action_mode_close_button);
if (layout == null) {
int id = Resources.getSystem().getIdentifier(
"action_mode_close_button", "id", "android");
layout = (LinearLayout) activity.findViewById(id);
}
if (layout != null && layout.getChildCount() > 1) {
TextView label = (TextView) layout.getChildAt(1);
if (label != null) label.setTextColor(Color.RED);
}
}
}
}.execute();
return false;
}
Worked with two devices pre and post Android 4.0.

Related

Setting custom font to Dialog

We are trying to set the title of a dialog to have a custom font so we tried doing a theme with the following:
<style name="DialogTheme" parent="Theme.AppCompat.Dialog.Alert">
<item name="android:windowTitleStyle">#style/Dialog.Title</item>
</style>
<style name="Dialog.Title" parent="RtlOverlay.DialogWindowTitle.AppCompat">
<item name="android:textAppearance">#style/Dialog.TextAppearance.Title</item>
</style>
<style name="Dialog.TextAppearance.Title" parent="TextAppearance.AppCompat.Title">
<item name="fontFamily">#font/custom_font</item>
</style>
And we are creating the dialog builder like this:
AlertDialog.Builder(ContextThemeWrapper(context, R.style.DialogTheme), R.style.DialogTheme)
The style and text appearance are completely ignored. For other styles, it seems to be working.
We managed to make it work by copying the layout used by AppCompat and adding it as a custom title:
<?xml version="1.0" encoding="utf-8"?>
<!-- modified from AppCompat's abc_alert_dialog_title_material.xml -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical|start|left"
android:paddingLeft="?attr/dialogPreferredPadding"
android:paddingRight="?attr/dialogPreferredPadding"
android:paddingTop="#dimen/abc_dialog_padding_top_material">
<TextView
android:id="#+id/alertTitle"
style="#style/Dialog.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="Some title" />
</FrameLayout>
Based on this: abc_alert_dialog_title_material.xml
Instead of using DialogTitle here we are using TextView. What DialogTitle is overloading onMeasure with the following:
final Layout layout = getLayout();
if (layout != null) {
final int lineCount = layout.getLineCount();
if (lineCount > 0) {
final int ellipsisCount = layout.getEllipsisCount(lineCount - 1);
if (ellipsisCount > 0) {
setSingleLine(false);
setMaxLines(2);
final TypedArray a = getContext().obtainStyledAttributes(null,
R.styleable.TextAppearance,
android.R.attr.textAppearanceMedium,
android.R.style.TextAppearance_Medium);
final int textSize = a.getDimensionPixelSize(
R.styleable.TextAppearance_android_textSize, 0);
if (textSize != 0) {
// textSize is already expressed in pixels
setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
}
a.recycle();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
We think it's related with it setting the text size, but we are not sure. We have a working hack (with the copied layout). However, we would prefer to know how we can make it work with xml styles and themes instead of having to change the default behaviour.
From my answer to another question I just posted recently, this is how I was able to programmatically set the font of all attributes of an AlertDialog. Hope it works for you.
Typeface semibold = ResourcesCompat.getFont(this, R.font.product_bold);
Typeface regular = ResourcesCompat.getFont(this, R.font.product_regular);
AlertDialog myDialog = new AlertDialog.Builder(this).setTitle("Your title")
.setMessage("Your message.")
.setPositiveButton("Your button", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//your code
}
}).show();
int titleText = getResources().getIdentifier("alertTitle", "id", "android");
((TextView) myDialog.getWindow().findViewById(titleText)).setTypeface(semibold);
TextView dialogMessage = myDialog.getWindow().findViewById(android.R.id.message);
Button dialogButton = myDialog.getWindow().findViewById(android.R.id.button1);
dialogMessage.setTypeface(regular);
dialogButton.setTypeface(semibold);
Confirmed working on Android 9.0, can't claim for it to work on older APIs.
Another method of programmatically setting the title with a TextView, this is how I used to do it:
TextView alertTitle = findViewById(R.id.alertTextTemp);
if (alertTitle.getParent() != null) {
((ViewGroup) alertTitle.getParent()).removeView(alertTitle);
}
alertTitle.setText("Warning");
alertTitle.setVisibility(View.VISIBLE);
alertTitle.setTypeface(semibold);
Then when you create your dialog, you set a custom title with:
new AlertDialog.Builder(MainActivity.this).setCustomTitle(alertTitle)...
alertTitle being a TextView in your activity_main.xml file:
<TextView
android:id="#+id/alertTextTemp"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"
android:paddingLeft="23dp"
android:paddingTop="22dp"
android:textColor="#000000" />
Those attributes on the TextView I had to fine tune to get it as close to the default title margins and size as possible, you might want to verify it works for you.

Show entire bottom sheet with EditText above Keyboard

I'm implementing a UI where a bottom sheet will appear above the keyboard with an EditText for the user to enter a value. The problem is the View is being partially overlapped by the keyboard, covering up the bottom of the bottom sheet.
Here is the Bottom Sheet and no keyboard.
Here is the Bottom Sheet with the keyboard showing.
What's the best method to ensure the entire Bottom Sheet is shown?
Thanks.
Just reposting #jblejder from this question Keyboard hides BottomSheetDialogFragment since it worked for me, to make it easier for others to find:
The most convenient way that I found to change this is by creating style:
<style name="DialogStyle" parent="Theme.Design.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:statusBarColor">#android:color/transparent</item>
<item name="android:windowSoftInputMode">adjustResize</item>
</style>
And set this in onCreate method of your BottomSheetDialogFragment:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogStyle)
}
This is how it looks on my device:
==== UPDATE ====
As already mentioned in the Comments a few times, you might also need to set the state of the BottomSheetDialog to STATE_EXPANDED like in Nordknight's answer below
dialog = new BottomSheetDialog(getContext(), R.style.BottomSheetDialog);
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
#Override
public void onShow(DialogInterface dialog) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = d.findViewById(R.id.design_bottom_sheet);
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
},0);
}
});
This might be a redundant answer. Although just pointing out the issue.
If you're using BottomSheetDialogFragment, the only way is to enable the attribute android:windowIsFloating to true. This will enable the whole window to be on top of whatever is trying to take the space behind it.
<style name="BottomSheetDialogThemeNoFloating" parent="Theme.Design.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:windowSoftInputMode">adjustResize|stateVisible</item>
</style>
Then in your onCreate() of your dialog, set this style.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// set the window no floating style
setStyle(DialogFragment.STYLE_NORMAL, R.style.AppRoundedBottomSheetDialogThemeNoFloating)
}
This is handy for those who frequently use bottom sheets and may want to deal with EditText and soft keyboard overlapping each other.
Note: The class KeyboardUtil by mikepenz has an issue in which on certain phones, the content view with input field is automatically pushed above keyboard despite giving bottom padding to the whole content view supplied.
dialog = new BottomSheetDialog(getContext(), R.style.BottomSheetDialog);
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
#Override
public void onShow(DialogInterface dialog) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = d.findViewById(R.id.design_bottom_sheet);
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
},0);
}
});
This code works fine at Fragment's onCreateView method (thanks for ADM)
Some answers seem to do the trick better than others but will need modification when using the new material design components instead of the older support libraries while also using kotlin
Hope this will help someone.
BottomSheetDialog(this, R.style.DialogStyle).apply {
setContentView(layoutInflater.inflate(R.layout.bottom_sheet, null))
window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
findViewById<EditText>(R.id.time_et)?.requestFocus()
show()
}
layout/bottom_sheet.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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="wrap_content"
android:background="#ffffff"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Time"
android:textColor="#000000"
android:textSize="24sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<EditText
android:id="#+id/time_et"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="numberSigned"
android:minWidth="50dp"
android:text="15" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:text="min" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="#000"
android:text="Save"
android:textColor="#fff" />
</LinearLayout>
</ScrollView>
styes.xml (Split for v-21 for using statusBarColor)
<style name="DialogStyle" parent="Theme.MaterialComponents.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:statusBarColor">#android:color/transparent</item>
<item name="android:windowSoftInputMode">adjustResize</item>
</style>
A BottomSheetDialog can be helpful for this. it will open with Softkeyboard open with focus on edit text.But user can still close the Softkeyboard and Dialog will be reset to Bottom. Again focusing will make dialog appear at top of Softkeyboard.
public void showDialog() {
final BottomSheetDialog dialog=new BottomSheetDialog(this);
dialog.setContentView(R.layout.item_dialog);
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
dialog.show();
}
You can make the BottomSheetDialog expanded over keyboard . But for this you need to call it after SoftKeyboard Open. the Expand code is .
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
I have tested it on DialogInterface.OnShowListener() but its not working . Tested with it 1 second delay its working . But Delay is not the solution . You need to figure out the on which action you should expand the dialog.
final BottomSheetDialog dialog=new BottomSheetDialog(this);
dialog.setContentView(R.layout.item_dialog);
dialog.getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE|
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
FrameLayout bottomSheet = (FrameLayout) dialog.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
}
},2000);
dialog.show();
An updated answer for those using Material Components theme, and also an improved answer to remove the need to add anything into each dialog's onCreate().
In your main AppTheme style, you can add the attribute bottomSheetDialogTheme to apply the style to all of your BottomSheetDialogFragments:
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
<item name="colorPrimary">#color/primary</item>
<item name="colorPrimaryDark">#color/primary_dark</item>
<item name="colorAccent">#color/accent</item>
<item name="bottomSheetDialogTheme">#style/BottomSheetDialogStyle</item>
</style>
So with the above, no need to add anything to your BottomSheetDialogFragment code.
And then, as previous answers, your Dialog style, noting to also match the style to the same Material Components library (or you'll get some weird looking buttons, edittexts etc):
<style name="BottomSheetDialogStyle" parent="Theme.MaterialComponents.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:windowSoftInputMode">adjustResize</item>
<item name="android:statusBarColor">#android:color/transparent</item>
<item name="colorPrimary">#color/primary</item>
<item name="colorPrimaryDark">#color/primary_dark</item>
<item name="colorAccent">#color/accent</item>
</style>
Note that I am adding my app theme colors back in here; as you can't have multiple inheritance in Android styles, you may want these colors defining here so any buttons and accents align with the rest of your app.
private fun enterMobileNumberPopUp() {
val dialog = BottomSheetDialog(this,R.style.DialogStyle)
val view = layoutInflater.inflate(R.layout.layout_otp, null)
dialog.setContentView(view)
dialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
dialog.show()}
THIS IS THE MOST EASY WAY AND BEST WAY TO HANDLE BOTTOM SHEET DIALOG
YOU CAN CALL THIS IN METHOD
<style name="DialogStyle" parent="Theme.Design.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:statusBarColor">#android:color/transparent</item>
<item name="android:windowSoftInputMode">adjustResize</item>
Style reference
This one working
BottomSheetDialog dialog = new BottomSheetDialog(this, R.style.DialogStyle);
View sheetView = getLayoutInflater().inflate(R.layout.dialog_remark, null);
Objects.requireNonNull(dialog.getWindow())
.setSoftInputMode(SOFT_INPUT_STATE_VISIBLE);
dialog.setContentView(sheetView);
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
#Override
public void onShow(DialogInterface dialog) {
BottomSheetDialog d = (BottomSheetDialog) dialog;
View bottomSheetInternal = d.findViewById(com.google.android.material.R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheetInternal).setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
dialog.show();
add this style to your styles.xml
<style name="DialogStyle" parent="Theme.Design.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:statusBarColor">#android:color/transparent</item>
<item name="android:windowSoftInputMode">adjustPan</item>
</style>
add your layout like this
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="#+id/scrollview"
android:layout_height="match_parent">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
android:fontFamily="#font/montserratmedium"
android:text="Add Remarks"
android:textColor="#android:color/black"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="24dp"
android:fontFamily="#font/montserratmedium"
android:text="Branch"
android:textColor="#8B8B8B"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:fontFamily="#font/montserratmedium"
android:text="BLR-CO-SINDHUBHAVAN-384"
android:textColor="#android:color/black"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="24dp"
android:fontFamily="#font/montserratmedium"
android:text="Enter Remarks"
android:textColor="#8B8B8B"
android:textSize="18sp" />
<EditText
android:id="#+id/input_remark"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_marginTop="8dp"
android:background="#drawable/remark_inputbg"
android:gravity="start"
android:inputType="textMultiLine"
android:lines="5" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/action"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="2">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_weight="1"
android:background="#drawable/reset_bg"
android:padding="8dp"
android:text="CANCEL" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_weight="1"
android:background="#4F4DBB"
android:padding="8dp"
android:text="CANCEL"
android:textColor="#android:color/white" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</ScrollView>
bottomSheetDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
It will work for sure.
Just write the following programmatically
override fun setupDialog(dialog: Dialog, style: Int) {
super.setupDialog(dialog, style)
dialog.window?.setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE or
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
);
}
Kotlin, +viewBinding, +by using the accepted answer's dialog style
val bottomSheet = BottomSheetDialog(this, R.style.BottomSheetDialogStyle)
val binding = [YourSheetBinding].inflate(LayoutInflater.from(YourActivity.this))
bottomSheet.setContentView(binding.root)
bottomSheet.behavior.state = BottomSheetBehavior.STATE_EXPANDED
bottomSheet.show()
override fun onStart() {
super.onStart()
bottomSheetBehavior?.skipCollapsed = true
bottomSheetBehavior?.state = BottomSheetBehavior.STATE_EXPANDED
}
Putting this in BottomSheet helped without setting styles and without ScrollView
(correct me if I'm wrong or if I'm missing something)
This trick solved me
in manifest put
android:windowSoftInputMode="adjustPan"
in your activity
and
bottomSheetDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
bottomSheetDialog.getBehavior().setState(BottomSheetBehavior.STATE_EXPANDED);
For this, I found an AlertDialog worked best. While it doesn't sit flush against the bottom or side of the screen, it still looks good enough.
First, create the AlertDialog with your view.
val view = LayoutInflater.from(context).inflate(R.layout.alert, null)
dialog = AlertDialog.Builder(context)
.setView(view)
.create()
Next, set the gravity.
dialog.window.attributes.gravity = Gravity.BOTTOM
And finally, show it.
dialog.show()
You can also bind the keyboard to stay with the dialog, by using an onDismissListener.
After showing the AlertDialog, I force up the keyboard.
Call this method, passing in your EditText.
fun showKeyboard(view: View?) {
if (view == null) return;
val imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
}
And for dismissing within the onDismissListener.
private fun hideKeyboard() {
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0)
}
My answer might be useful for someone who is still looking for solution. If keyboard is covering edittext in BottomSheetDialogFragment then in setupDialog() method create instance of a class KeyboardUtil and pass your rootview.
#Override
public void setupDialog(final Dialog dialog, int style) {
super.setupDialog(dialog, style);
View view = View.inflate(getActivity(), R.layout.reopen_dialog_layout, null);
new KeyboardUtil(getActivity(), view);
}
Create a new class
public class KeyboardUtil {
private View decorView;
private View contentView;
//a small helper to allow showing the editText focus
ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
decorView.getWindowVisibleDisplayFrame(r);
//get screen height and calculate the difference with the useable area from the r
int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels;
int diff = height - r.bottom;
//if it could be a keyboard add the padding to the view
if (diff != 0) {
// if the use-able screen height differs from the total screen height we assume that it shows a keyboard now
//check if the padding is 0 (if yes set the padding for the keyboard)
if (contentView.getPaddingBottom() != diff) {
//set the padding of the contentView for the keyboard
contentView.setPadding(0, 0, 0, diff);
}
} else {
//check if the padding is != 0 (if yes reset the padding)
if (contentView.getPaddingBottom() != 0) {
//reset the padding of the contentView
contentView.setPadding(0, 0, 0, 0);
}
}
}
};
public KeyboardUtil(Activity act, View contentView) {
this.decorView = act.getWindow().getDecorView();
this.contentView = contentView;
//only required on newer android versions. it was working on API level 19
if (Build.VERSION.SDK_INT >= 19) {
decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
/**
* Helper to hide the keyboard
*
* #param act
*/
public static void hideKeyboard(Activity act) {
if (act != null && act.getCurrentFocus() != null) {
InputMethodManager inputMethodManager = (InputMethodManager) act.getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(act.getCurrentFocus().getWindowToken(), 0);
}
}
public void enable() {
if (Build.VERSION.SDK_INT >= 19) {
decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
public void disable() {
if (Build.VERSION.SDK_INT >= 19) {
decorView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
}
See https://stackoverflow.com/a/61813321/2914140:
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
dialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
return dialog
}
But if a layout is not tall enough, you can use https://stackoverflow.com/a/66287187/2914140 instead. It will open BottomSheetDialog almost fullscreen:
<style name="BottomSheetDialogTheme" parent="Theme.MaterialComponents.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:windowSoftInputMode">adjustResize|stateVisible</item>
</style>
Fiddling with BottomSheetDialogFragmentwasn't really worth it. So I just changed it to a simple DialogFragment and just set its gravity to bottom:
window.setGravity(Gravity.BOTTOM);
Worked like a charm.

How can I fix the Spinner style for Android 4.x placed on top of the Toolbar

According to Android documentation, Material Design style is supported for Spinner widget.
So I decided to use it in my app placing it on top of the Toolbar.
layout/activity_base.xml
<android.support.v7.widget.Toolbar
android:id="#+id/my_awesome_toolbar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="5dp"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light">
<Spinner
android:id="#+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</android.support.v7.widget.Toolbar>
Activity theme
<style name="BaseAppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">#color/omni_primary_color</item>
<item name="colorPrimaryDark">#color/omni_primary_color_dark</item>
<item name="colorAccent">#color/omni_accent_color</item>
</style>
BaseActivity.java
public class BaseActivity extends ActionBarActivity {
#InjectView(R.id.my_awesome_toolbar)
Toolbar mToolbar;
#InjectView(R.id.spinner)
Spinner spinner;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base);
ButterKnife.inject(this);
//setup toolbar
setSupportActionBar(mToolbar);
getSupportActionBar().setDisplayShowTitleEnabled(false);
mToolbar.setNavigationIcon(R.drawable.ic_action_navigation_menu);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(mToolbar.getContext(),
R.array.planets_array, R.layout.support_simple_spinner_dropdown_item);
adapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
}
}
On Lollipop spinner and dropdown looks fine, although dropdown background color is black comparing to menu dropdown which is white. I guess that app:popupTheme="#style/ThemeOverlay.AppCompat.Light" is not propagated to the spinner.
Android 5.0
Now the big problem is with Android 4.x where dropdown background color is white(popupTheme propagated?) and icon next to the spinner is black.
Android 4.4
How can I set it properly in the XML or implement in the code to make it work on both Android 5 and 4? Ideally, I would like to have both looks like on Android 5 but with white spinner dropdown(like Setting menu dropdown).
Update
I have noticed that setting property colorControlNormal affects spinner's filter icon. If someone finds out how to make use of that for Spinner(without changing other content controls), then I would have my solution combining that finding with #Sven answer.
Update
The following change fixes the problem for spinner text and popup color. So the only problem to the final solution is the filter icon.
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getSupportActionBar().getThemedContext(),
R.array.planets_array, R.layout.support_simple_spinner_dropdown_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Update
I found that filter icon is actually a part of android:background specified for the spinner and it's transparent. Providing own background would fix it e.g.
<item name="android:background">?android:selectableItemBackground</item>
Mystery solved!
The last piece of the puzzle is the popup on Android 5 that has black background and white text but I guess it can be solved with custom layout. If no one provides full answer I will do it myself and mark as accepted.
I know this is late but I came accross this question when I encountered this problem myself and I found a solution in the BrowseSessionsActivity of the Google I/O 2014 app and adapted it.
Layouts
toolbar_spinner.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<Spinner
android:id="#+id/toolbar_spinner"
style="#style/Widget.MyApp.HeaderBar.Spinner"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
</LinearLayout>
toolbar_spinner_item_actionbar.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="8dp"
android:drawableRight="#drawable/spinner_triangle"
android:fontFamily="sans-serif"
android:paddingLeft="16dp"
android:paddingRight="4dp"
android:textColor="#ffffffff"
android:textSize="18dp"
android:textStyle="bold"/>
</LinearLayout>
The spinner_triangle drawable can be found here.
toolbar_spinner_item_dropdown.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="48dp"
android:drawablePadding="8dp"
android:gravity="center_vertical|start"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textColor="#ff333333"
android:textSize="16sp"/>
</LinearLayout>
Styles
toolbar_spinner.xml uses the following style.
<style name="Widget.MyApp.HeaderBar.Spinner" parent="Widget.AppCompat.Light.Spinner.DropDown.ActionBar">
<item name="android:background">?android:selectableItemBackground</item>
<item name="android:dropDownSelector">?android:selectableItemBackground</item>
<item name="android:divider">#null</item>
<item name="android:overlapAnchor">true</item>
</style>
Adapter
This adapter will need to be changed to match your own needs. getTitle() returns the text for each item shown in the spinner.
private class YourObjectSpinnerAdapter extends BaseAdapter {
private List<YourObject> mItems = new ArrayList<>();
public void clear() {
mItems.clear();
}
public void addItem(YourObject yourObject) {
mItems.add(yourObject);
}
public void addItems(List<YourObject> yourObjectList) {
mItems.addAll(yourObjectList);
}
#Override
public int getCount() {
return mItems.size();
}
#Override
public Object getItem(int position) {
return mItems.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getDropDownView(int position, View view, ViewGroup parent) {
if (view == null || !view.getTag().toString().equals("DROPDOWN")) {
view = getLayoutInflater().inflate(R.layout.toolbar_spinner_item_dropdown, parent, false);
view.setTag("DROPDOWN");
}
TextView textView = (TextView) view.findViewById(android.R.id.text1);
textView.setText(getTitle(position));
return view;
}
#Override
public View getView(int position, View view, ViewGroup parent) {
if (view == null || !view.getTag().toString().equals("NON_DROPDOWN")) {
view = getLayoutInflater().inflate(R.layout.
toolbar_spinner_item_actionbar, parent, false);
view.setTag("NON_DROPDOWN");
}
TextView textView = (TextView) view.findViewById(android.R.id.text1);
textView.setText(getTitle(position));
return view;
}
private String getTitle(int position) {
return position >= 0 && position < mItems.size() ? mItems.get(position).title : "";
}
}
Adding the Spinner to Your Toolbar
Toolbar toolbar = getActionBarToolbar();
View spinnerContainer = LayoutInflater.from(this).inflate(R.layout.toolbar_spinner,
toolbar, false);
ActionBar.LayoutParams lp = new ActionBar.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
toolbar.addView(spinnerContainer, lp);
YourObjectSpinnerAdapter spinnerAdapter = new YourObjectSpinnerAdapter();
spinnerAdapter.addItems(getMyObjectSpinnerData());
Spinner spinner = (Spinner) spinnerContainer.findViewById(R.id.toolbar_spinner);
spinner.setAdapter(spinnerAdapter);
Result
Don't implement Spinner in Xml
final ArrayAdapter spinnerAdapter = ArrayAdapter.createFromResource(getSupportActionBar().getThemedContext(),
R.array.main_navigation_list, R.layout.spinner_text);
spinnerAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
mNavigationTags = getResources().getStringArray(R.array.main_navigation_list);
mNavigationSpinner = new Spinner(getSupportActionBar().getThemedContext());
mNavigationSpinner.setAdapter(spinnerAdapter);
mNavigationSpinner.setOnItemSelectedListener(this);
mToolbar.addView(mNavigationSpinner);
This way the icon next to spinner will be white
Sorry for my poor English. :)
I think it is better to directly create the spinner in Toolbar.
Here is a example in my fragment.
public class Testfragment1 extends Fragment {
Toolbar mToolbar;
Spinner mSpinner;
.....
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
.......
mToolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);
//you can also set the style with the constructor
mSpinner = new Spinner(getActivity());
String[] frags = new String[]{
"category1",
"category2",
"category3",
};
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item_1,frags);
mSpinner.setAdapter(arrayAdapter);
mToolbar.addView(mSpinner);
return inflater.inflate(R.layout.fragment_testfragment1, container, false);
}
.........
#Override
public void onDestroyView() {
super.onDestroyView();
if (mToolbar != null && mSpinner != null) {
mToolbar.removeView(mSpinner);
}
}
}
It looks fine on my android-4.1-device:
android-4.1-spinner
I am struggling with the exact same problem.
Try to change the dropdown view resource. At least, this fixed the text color issue for me - however the arrow icon color is still dark. So this is just a partial workaround.
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
A simple way that isn't perfect, but uniform enough for both 4.x and 5.0
I removed the <Spinner> from the layout files and added it programmatically - that allowed for the white triangle to show up properly.
I also created a dropdown item layout using the appcompat required color.
layout/spinner_dropdown_item.xml, note the android:background="#color/primaryColor"
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:gravity="center_vertical"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:background="#color/primaryColor"
android:minHeight="?android:attr/listPreferredItemHeightSmall" />
And in the activity:
SpinnerAdapter spinnerAdapter = ArrayAdapter.createFromResource(getApplicationContext(), R.array.your_array, R.layout.spinner_dropdown_item);
Spinner navigationSpinner = new Spinner(getSupportActionBar().getThemedContext());
navigationSpinner.setAdapter(spinnerAdapter);
toolbar.addView(navigationSpinner, 0);
It's not perfect and the items don't highlight when you click on them, but it's good enough while we wait for the future appcompat libraries to fix these problems (here's hoping anyway).
I spent two days on this problem, but now after reading many answers, I can post my solution. I've implemented two custom layouts for the spinner item and popup. Setting this attribute for spinner: android:background="?android:selectableItemBackground" the default spinner black arrow is hidden and we can use what we prefer. I used the method setDropDownVerticalOffset(int) to manage the popup position on pre Lollipop Android versions.
My app global theme is
<style name="AppTheme" parent="AppTheme.Base">
</style>
<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">#color/primary</item>
<item name="colorPrimaryDark">#color/primary_dark</item>
<item name="colorAccent">#color/accent</item>
<item name="android:windowBackground">#color/window_background</item>
</style>
Now, the activity layout that contains the toolbar and the spinner:
activity_main.xml
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true" >
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:elevation="4dp"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" >
<Spinner
android:id="#+id/spinner_rss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Light"
android:background="?android:selectableItemBackground" />
</android.support.v7.widget.Toolbar>
</RelativeLayout>
custom_spinner_toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/spinner_item_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:singleLine="true"
android:textColor="#android:color/white"
android:textAppearance="#style/TextAppearance.AppCompat.Title"
/>
<ImageView
android:contentDescription="#string/content_description_arrow_dropdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/spinner_item_text"
android:layout_toEndOf="#+id/spinner_item_text"
android:paddingTop="6dp"
android:src="#drawable/ic_arrow_drop_down_white_24dp" />
</RelativeLayout>
custom_spinner_dropdown_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<CheckedTextView
android:id="#+id/spinner_item_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:singleLine="true"
android:textColor="#android:color/black"
android:textSize="16sp" />
</LinearLayout>
SpinnerAdapter.java
public class SpinnerAdapter extends BaseAdapter
{
private Context mContext;
private List<String> mValuesList;
public SpinnerAdapter(Context mContext, List<String> mValuesList)
{
this.mContext = mContext;
this.mValuesList = mValuesList;
}
#Override
public int getCount()
{
return mValuesList.size();
}
#Override
public Object getItem(int position)
{
return mValuesList.get(position);
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
#Override
public View getDropDownView(int position, View view, ViewGroup parent)
{
if (view == null || !view.getTag().toString().equals("DROPDOWN"))
{
LayoutInflater inflater = LayoutInflater.from(mContext);
view = inflater.inflate(R.layout.custom_spinner_dropdown_item, parent, false);
view.setTag("DROPDOWN");
}
TextView textView = (TextView) view.findViewById(R.id.spinner_item_text);
textView.setText(getTitle(position));
return view;
}
#Override
public View getView(int position, View view, ViewGroup parent)
{
if (view == null || !view.getTag().toString().equals("NON_DROPDOWN"))
{
LayoutInflater inflater = LayoutInflater.from(mContext);
view = inflater.inflate(R.layout.custom_spinner_toolbar, parent, false);
view.setTag("NON_DROPDOWN");
}
TextView textView = (TextView) view.findViewById(R.id.spinner_item_text);
textView.setText(getTitle(position));
return view;
}
private String getTitle(int position)
{
return position >= 0 && position < mValuesList.size() ? mValuesList.get(position) : "";
}
}
Finally, the relevant part of activity source code:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
final ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
actionBar.setDisplayHomeAsUpEnabled(true);
mSpinner = (Spinner) findViewById(R.id.spinner_rss);
String[] items = getResources().getStringArray(R.array.spinner_rss_items);
List<String> spinnerItems = new ArrayList<String>();
for(int i = 0; i < items.length; i++)
{
spinnerItems.add(items[i]);
}
SpinnerAdapter adapter = new SpinnerAdapter(actionBar.getThemedContext(), spinnerItems);
mSpinner.setAdapter(adapter);
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
{
mSpinner.setDropDownVerticalOffset(-116);
}
}
These are the results on Lollipop and Kitkat:
Hope it helps! :)
Use android:dropDownVerticalOffset property inside spinner to give spacing from top.
<Spinner
android:id="#+id/spnrLanguage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:background="#drawable/ic_dropdown"
android:padding="5dp"
android:spinnerMode="dropdown"
android:dropDownVerticalOffset="50dp"
/>
Don't forgot to set android:spinnerMode="dropdown" though it won't work in spinnerMode= dialog
Can you not do this?
Custom xml file for spinner item: your_spinner.xml:
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#000"
android:background="#FFF"
/>
Use this to show spinner items:
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.your_spinner,list);
Then remove drop down resource.
Had the exact same issue with the spinner's
What i did was to add a custom theme to spinner
<Spinner
android:id="#+id/spinner1"
android:layout_width="match_parent"
android:layout_height="30sp"
android:entries="#array/guest_type"
android:prompt="#string/guesttype"
android:theme="#style/AppTheme1" />
styles.xml
<style name="AppTheme1" parent="Theme.AppCompat.Light">
<item name="android:spinnerDropDownItemStyle">#style/mySpinnerItemStyle</item>
</style>
<style name="mySpinnerItemStyle" parent="#android:style/Widget.Holo.DropDownItem.Spinner">
<item name="android:textColor">#000000</item>
</style>
For correct Spinner icon tinting you can also just inflate the spinner from code:
spinner_toolbar.xml:
<?xml version="1.0" encoding="utf-8"?>
<Spinner xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/spinner_toolbar"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
Then you have to attach the Spinner to the Toolbar in your Activity:
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getSupportActionBar().getThemedContext(),
R.array.planets_array, R.layout.support_simple_spinner_dropdown_item);
adapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
// we inflate the spinner with the themed Toolbar context -> correct icon tinting
LayoutInflater.from(getSupportActionBar().getThemedContext()).inflate(R.layout.spinner_toolbar, tb, true);
Spinner spinner = (Spinner) toolbar.findViewById(R.id.spinner_toolbar);
spinner.setAdapter(adapter);
However, this uses the app:theme instead of the app:popupTheme for the whole Spinner, including the dropdown menu.
Hence, the Spinner icon and text will be colored correctly, but the dropdown menu also has the style of the toolbar and not of the popupTheme.
So if you want to have a dark Toolbar and a light dropdown menu, you would need to fix the dropdown style somehow, for example by creating a custom style for the spinner that specifies a white background and a custom dropdown view with a dark text color.
Maybe somebody else has a better solution on how the app:popupTheme can be propagated to the Spinner dropdown menu.
You can fix dropdown position (will show on the top of toolbar, like menu) for Android 4 using this code:
<Spinner
android:id="#+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:dropDownVerticalOffset="-56dp"/>
To pick up on this, I was having similar problems. My main problem was the text in my toolbar was smaller than the usual title dimensions and the wrong colour. Screenshot here http://s27.postimg.org/v24x1aw43/Screen_Shot_2015_01_11_at_13_36_04.png
The dropdown menu was ok, but I will go through the customisation of that as well.
Let me also make clear this fix is mostly based on #Daniel B's fix, however does not require the custom adapter, as far as I can tell nothing is broken, but I give no guarantees!
Add a normal spinner item into the XML layout file (within the toolbar).
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="5dp"
android:minHeight="?attr/actionBarSize"
android:background="#color/colorPrimary"
app:theme="#style/GalaxyZooThemeToolbarDarkOverflow"
app:popupTheme="#style/Theme.AppCompat"
>
<Spinner
android:id="#+id/spinner_nav"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</android.support.v7.widget.Toolbar>
Create new layout file toolbar_spinner_item_actionbar.xml (This will be the stuff showing for the spinner in the toolbar)
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:id="#android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="20dp"
android:fontFamily="sans-serif"
android:paddingLeft="#dimen/abc_action_bar_default_padding_material"
android:paddingRight="4dp"
android:textColor="#color/colorDark"
android:textSize="#dimen/abc_text_size_title_material_toolbar"
xmlns:android="http://schemas.android.com/apk/res/android"/>
<!-- android:drawableRight="#drawable/spinner_triangle" -->
The adapter for your spinner remains pretty much the same, however switch the layout from the standard android.R.layout.simple_spinner_dropdown_item to R.layout.toolbar_spinner_item_actionbar. This will apply your custom look for the toolbar text.
In this example I have set the adapter.setDropDownViewResource to android.R.layout.simple_spinner_dropdown_item, this applies the standard theme defaults for the drop down list, which I am happy with.
ArrayAdapter<String> set1Adapter = new ArrayAdapter<String>(RoutineDetailsActivity.this, R.layout.toolbar_spinner_item_actionbar, set1Actual);
set1Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mWeekSpinner.setAdapter(set1Adapter);
That's basically it, result here [can't attach image or add another link as my rep is too low! Will add in comment]
. You could stop here, however you may want to change the colour of the dropdown arrow.
Technically it is the correct tinting for my app, however, as my primary colour is already the colour for the toolbar, it would make sense to customise the arrow.
Setup custom arrow drawable
Add this line drawable line 'android:drawableRight="#drawable/spinner_triangle' into the toolbar_spinner_item_actionbar.xml made earlier. Now this could be any image, for now you could use Daniel B's white arrow resource here https://raw.githubusercontent.com/google/iosched/master/android/src/main/res/drawable-xxhdpi/spinner_triangle.png.
Running this will result in two arrows, the white arrow and the theme default. To solve this add the style below. Again this is pulled from Daniel B's code and could probably be abridged, but for now it works....
<style name="Widget.MyApp.HeaderBar.Spinner" parent="Widget.AppCompat.Light.Spinner.DropDown.ActionBar">
<item name="android:background">?android:selectableItemBackground</item>
<item name="android:dropDownSelector">?android:selectableItemBackground</item>
<item name="android:divider">#null</item>
<item name="android:overlapAnchor">true</item>
</style>
Apply the created style to the spinner...
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="5dp"
android:minHeight="?attr/actionBarSize"
android:background="#color/colorPrimary"
app:theme="#style/GalaxyZooThemeToolbarDarkOverflow"
app:popupTheme="#style/Theme.AppCompat"
>
<Spinner
android:id="#+id/spinner_nav"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/Widget.MyApp.HeaderBar.Spinner"/>
</android.support.v7.widget.Toolbar>
The result will be something like this [again can't attach or link, will add to comment]. The padding can be set from the file setup earlier, in my case I would need to change the arrow to match the icons.
Hope that makes some sort of sense.
When i used spinner it crashed (Android 2.3.3 - 2.3.7).
So i try to use TintSpinner now it's not crashing, Try your self as a Optional solution
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.internal.widget.TintSpinner
android:id="#+id/toolbar_spinner"
style="#style/HeaderBar.Spinner"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
</LinearLayout>
And use below code to cast your toolbar
View spinnerContainer = LayoutInflater.from(this).inflate(R.layout.toolbar_spinner, toolbarTop, false);
ActionBar.LayoutParams lp = new ActionBar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
toolbarTop.addView(spinnerContainer, lp);
ToolBarSpinnerAdapter spinnerAdapter = new ToolBarSpinnerAdapter(getLayoutInflater());
String[] items = getResources().getStringArray(R.array.action_dropdown);
spinnerAdapter.addItems(items);
TintSpinner mNavigationSpinner = (TintSpinner) spinnerContainer.findViewById(R.id.toolbar_spinner);
mNavigationSpinner.setAdapter(spinnerAdapter);
I've wasted hours on this issue. As far as I can tell, the above solutions all require copy/pasting large chunks of appcompat style code to reimplement basic details like touch states.
A relatively easy way to get native-like behaviour is to inflate the view programmatically to ensure it gets the correct theme, e.g.:
// Activity has context with 'Theme.AppCompat.Light.NoActionBar'
spinner = new AppCompatSpinner(getActivity());
toolbar.addView(spinner);
To get the triangle to be white rather than colorControlNormal, I've applied a ColorStateList tint to the background:
ViewCompat.setBackgroundTintList(spinner, resources.getColorStateList(R.drawable.bg_toolbar_spinner)
bg_toolbar_spinner.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#color/accent" android:state_pressed="true"/>
<item android:color="#android:color/white"/>
</selector>
I solved it by creating new values for version 21 and 23 and adding new attribute in the spinner style android:dropDownVerticalOffset and delete it from the default style file. (my case is not related to toolbar) it's for normal spinner.
Add this style in folders 23 and 21
<style name="spinner_style">
<item name="android:background">#drawable/background_spinner</item>
<item name="android:dropDownVerticalOffset">30dip</item>
</style>
It's working perfectly on all versions. Hope this works with you!

Themes and AlertDialog with list adapter

I have troubles with AlertDialog and my custom themes. I have 2 themes Black and Light defined like this
<style name="AppThemeBlack" parent="android:Theme.Black">
...................
</style>
<style name="AppThemeLight" parent="android:Theme.Light">
...................
</style>
I have base activity that apply theme according to preferences
public class BaseActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
Util.SetTheme(this, Config.getInstance().getUITheme());
super.onCreate(savedInstanceState);
}
}
.............................
private static int GetThemeIdByName(String themeName)
{
if (themeName.equals("AppThemeLight"))
return R.style.AppThemeLight;
else if (themeName.equals("AppThemeBlack"))
return R.style.AppThemeBlack;
return R.style.AppThemeLight;
}
public static void SetTheme(Context context, String themeName)
{
int id = GetThemeIdByName(themeName);
context.setTheme(id);
}
All activities inherited from this BaseActivity. So my themes applied for every activity in the application.
But there is problem with AlertDialog
By default in AndroidManifest defined Light theme
<application android:theme="#style/AppThemeLight" >
I create AlertDialog like this
AlertDialog.Builder dlg = new AlertDialog.Builder(this);
ObjectListAdapter<TPartnerSimple> adapter = new ObjectListAdapter<TPartnerSimple>(this, list);
dlg.setAdapter(adapter, new DialogInterface.OnClickListener(){....});
dlg.show();
This adapter use item layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/txtName_ObjectListItem"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:text="Position 1"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
When application use light theme list displayed with white background and black text.
But if I apply black theme it list displayed with white background and WHITE text (as defined in BlackTheme)
So I can't see items text
Why dialog uses white text defined in black theme and not uses black background defined there?
How could I change background of dialog to black to see items text using themes?

AlertDialog setCustomTitle styling to match standard AlertDialog title

I'm working on an Android app and I have an AlertDialog subclass. I would like to put 2 ImageButtons on the right side of the title area of the dialog (similar to an the ActionBar in an Activity). I'm using setCustomTitle() to do this, which replaces the title area with a custom view of my own creation. This works fine, but the styling of my custom title area is not the same as the standard title styling (height, color, separator, etc).
My question is: with the understanding that styling varies by OS version and manufacturer, how can I style my custom title in the dialog so that it will match the standard title styling for other AlertDialogs?
Here is an image of anAlertDialog with standard styling (this is from ICS, but I want to be able to match any variant -- not this particular style)
And here is an image of an AlertDialog with custom title and buttons (note how the title height and color don't match the standard dialog)
EDIT: I can't just add the ImageButtons to the standard title view, because I don't have access to it. If you know of a (reliable, non-hack) method for me to add buttons to the standard title area, I would accept that as well.
Given that there is new interest in this question, let me elaborate about how I "solved" this.
First, I use ActionBarSherlock in my app. This is not necessary, I suppose, though it helps a lot because the styles and themes defined in the ABS project allow me to mimic the Holo theme on pre-ICS devices, which provides a consistent experience in the app.
Second, my "dialog" is no longer a dialog -- it's an activity themed as a dialog. This makes manipulation of the view hierarchy simpler, because I have complete control. So adding buttons to the title area is now trivial.
Here are the screenshots (2.2 device and 4.1 emulator). Note that the only significant styling difference is the EditText, which I have chosen not to address.
Here is my onCreate in my dialog activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_tag);
setTitle(R.string.tag_dialog_title);
View sherlockTitle = findViewById(android.R.id.title);
if (sherlockTitle != null) {
sherlockTitle.setVisibility(View.GONE);
}
View sherlockDivider = findViewById(R.id.abs__titleDivider);
if (sherlockDivider != null) {
sherlockDivider.setVisibility(View.GONE);
}
// setup custom title area
final View titleArea = findViewById(R.id.dialog_custom_title_area);
if (titleArea != null) {
titleArea.setVisibility(View.VISIBLE);
TextView titleView = (TextView) titleArea.findViewById(R.id.custom_title);
if (titleView != null) {
titleView.setText(R.string.tag_dialog_title);
}
ImageButton cancelBtn = (ImageButton) titleArea.findViewById(R.id.cancel_btn);
cancelBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
cancelBtn.setVisibility(View.VISIBLE);
ImageButton okBtn = (ImageButton) titleArea.findViewById(R.id.ok_btn);
okBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// do stuff here
finish();
}
});
okBtn.setVisibility(View.VISIBLE);
}
}
And here is the relevant layout for the activity:
<LinearLayout
android:orientation="vertical"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<LinearLayout
android:id="#+id/dialog_custom_title_area"
android:orientation="vertical"
android:fitsSystemWindows="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingRight="10dp">
<TextView
android:id="#+id/custom_title" style="?android:attr/windowTitleStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minHeight="#dimen/abs__alert_dialog_title_height"
android:paddingLeft="16dip"
android:paddingRight="16dip"
android:textColor="#ffffff"
android:gravity="center_vertical|left" />
<ImageButton
android:id="#+id/ok_btn"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:minWidth="#dimen/abs__action_button_min_width"
android:minHeight="#dimen/abs__alert_dialog_title_height"
android:scaleType="center"
android:src="#drawable/ic_action_accept"
android:background="#drawable/abs__item_background_holo_dark"
android:visibility="visible"
android:layout_gravity="center_vertical"
android:contentDescription="#string/acc_done"/>
<ImageButton
android:id="#+id/cancel_btn"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:minWidth="#dimen/abs__action_button_min_width"
android:minHeight="#dimen/abs__alert_dialog_title_height"
android:scaleType="center"
android:src="#drawable/ic_action_cancel"
android:background="#drawable/abs__item_background_holo_dark"
android:visibility="visible"
android:layout_gravity="center_vertical"
android:contentDescription="#string/acc_cancel"
/>
</LinearLayout>
<View
android:id="#+id/dialog_title_divider"
android:layout_width="fill_parent"
android:layout_height="2dip"
android:background="#color/abs__holo_blue_light" />
</LinearLayout>
<RelativeLayout
android:id="#+id/list_suggestions_layout"
android:layout_height="wrap_content"
android:layout_width="fill_parent">
<!-- this is where the main dialog area is laid out -->
</RelativeLayout>
</LinearLayout>
And finally, in my AndroidManifext.xml, here is how I define my TagActivity:
<activity
android:icon="#drawable/ic_home"
android:name=".activity.TagActivity"
android:theme="#style/Theme.Sherlock.Dialog"/>
OK, maybe it is not the super perfect solution and maybe it is a bad solution, but I tried this on android 2.3.7 and android 4.1.2:
2.3.7 (real device)
4.1.2 (emulator)
We start by creating a dialog Title style to make sure we have some space for our icons:
res/values/dialogstyles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="Dialog" parent="#android:style/Theme.Dialog">
<item name="android:windowTitleStyle">#style/MyOwnDialogTitle</item>
</style>
<style name="MyOwnDialogTitle">
<!-- we need to make sure our images fit -->
<item name="android:layout_marginRight">100dp</item>
</style>
</resources>
res/values-v11/dialogstyles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="Dialog" parent="#android:style/Theme.Holo.Dialog">
<item name="android:windowTitleStyle">#style/MyOwnDialogTitle</item>
</style>
</resources>
Then we create our DialogFragment with two tricks:
set the style in the onCreate:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NORMAL, R.style.Dialog);
}
override onCreateView and add our layout (of buttons) to the Dialog (see comments)
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//we need the view to remove the tree observer (that's why it is final)
final View view = inflater.inflate(R.layout.dialog_custom, container);
getDialog().setTitle("Shush Dialog");
//register a layout listener to add our buttons
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#SuppressWarnings("deprecation")
#SuppressLint("NewApi")
#Override
public void onGlobalLayout() {
//inflate our buttons
View menu = LayoutInflater.from(getActivity()).inflate(R.layout.layout_mymenu, null);
//get the root view of the Dialog (I am pretty sure this is the weakest link)
FrameLayout fl = ((FrameLayout) getDialog().getWindow().getDecorView());
//get the height of the root view (to estimate the height of the title)
int height = fl.getHeight() - fl.getPaddingTop() - fl.getPaddingBottom();
//to estimate the height of the title, we subtract our view's height
//we are sure we have the heights btw because layout is done
height = height - view.getHeight();
//prepare the layout params for our view (this includes setting its width)
//setting the height is not necessary if we ensure it is small
//we could even add some padding but anyway!
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, height);
params.gravity = Gravity.RIGHT | Gravity.TOP;
//add the view and we are done
fl.addView(menu, params);
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
else
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
return view;
}
Alright if it just images, then you just have ensure that everything that you create in xml is scaled by density pixels or DP for short. Most simple coding that sets paint are usually set by pixels as well and may need a manual coding version to density pixels.

Categories

Resources