Can't write text on forced keyboard Android Xamarin - android

I'm doing my own custom rich text editor in Android using Xamarin, with possibilities of adding/removing bold, underline, italic, color (etc) attributes on the selection. I was losing focus every time I clicked on a button that set one of these attributes so I found the solution with the soft keyboard.
But with the soft keyboard that opens
I can't write more text
even though I have forced the focus on the EditText. The keys pressed do nothing.
Here's the code I'm using to force the soft keyboard to show :
_myEditText.FocusChange += _MyEditText_OnFocusChanged;
and
private void _editor_FocusChange(object sender, FocusChangeEventArgs e)
{
_myEditText.RequestFocus();
InputMethodManager imm = (InputMethodManager)Context.GetSystemService(Context.InputMethodService);
imm.ToggleSoftInput(ShowFlags.Implicit, 0);
}
Is there a way to keep the keyboard open and being able to write new text in the EditText? I've tried with the ShowSoftInput() and the ShowFlags.Forced tag and even the ShowSoftInputOnFocus property on _myEditText but results are the same.
EDIT
I made a sample in Native Android with Android Studio and the keyboard works perfectly OK even without the use of the InputMethodManager ... what am I missing. Is there something in Xamarin that's not working properly ? Because in native it has the exact behaviour that I'm unable to make in xamarin.
EDIT 2
It seems related to the fact I'm using the AppCompat.ViewRenderer of Xamarin.Android and not the basic ViewRenderer. On my way to test it.
EDIT 3
After a long time testing every environment and possibilities, I've come to a conclusion that the code IS working the same way as in a native application but with a custom renderer,
ONLY if my clicks on the buttons are at least ~0.5s long.
If a do an instant click, I lose the focus of my EditText, but if I stay pressed on the button a little longer, the button action is fired, the keyboard stays open and the selection is impacted by the style changement.
Here is some basic code to reproduce the problem as easy as possible.
Just create a basic forms app. And then add the 3 following class
My forms custom view : (empty)
public class RichTextEditor : View
{
}
My android renderer :
using Android.App;
using TestFocusApplicationForms.CustomViews;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(RichTextEditor), typeof(TestFocusApplicationForms.Droid.RichTextEditorRenderer_Droid))]
namespace TestFocusApplicationForms.Droid
{
public class RichTextEditorRenderer_Droid : Xamarin.Forms.Platform.Android.ViewRenderer<RichTextEditor, Android.Views.View>
{
protected override void OnElementChanged(ElementChangedEventArgs<RichTextEditor> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
{
return;
}
if (Control == null)
{
Activity activity = this.Context as Activity;
var view = activity.LayoutInflater.Inflate(Resource.Layout.TestFocus_Layout, this, false);
SetNativeControl(view);
}
}
}
}
And the TestFocus_Layout.axml file corresponding :
<?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="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:orientation="horizontal"
android:layout_gravity="top|end">
<Button
android:layout_gravity="end"
android:layout_width="44dp"
android:layout_height="44dp"
android:text="B"
android:id="#+id/boldButton" />
<Button
android:layout_gravity="end"
android:layout_width="44dp"
android:layout_height="44dp"
android:text="I"
android:id="#+id/italicButton" />
</LinearLayout>
<EditText
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="top"
android:hint="Texte riche"
android:layout_gravity="bottom"
android:id="#+id/myEditText"
android:singleLine="false"
android:imeOptions="actionNone" />
</LinearLayout>

I've found a solution, it might not be the best in term of long term design but it works.
I made my own VisualElementRenderer for my RichTextEditor Class, and removed all unnecessary code and more important, removed code overriding the default focus behaviour of the element.
Also, it seems the fact that inflating the layout from a .axml file would cause different behaviour as creating the views programmatically, so I made a RichTextEditor_Droid class containing simple conversion of my .axml in C#. And I removed the FocusChange eventHandler on my EditText.
Here is my renderer :
public class RichTextEditorRenderer_Droid :
Xamarin.Forms.Platform.Android.VisualElementRenderer<RichTextEditor>
{
public Android.Views.View NativeView { get; private set; }
public RichTextEditorRenderer_Droid(Context context) : base(context)
{
}
public RichTextEditorRenderer_Droid()
{
}
protected override void OnElementChanged(
ElementChangedEventArgs<RichTextEditor> e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
{
return;
}
if (NativeView == null)
{
Xamarin.Forms.Application.Current.On<Xamarin.Forms.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);
var activity = Context as Activity;
var textPad = new RichTextEditor_Droid(this.Context);
var textPadLayoutParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MatchParent,
ViewGroup.LayoutParams.MatchParent);
activity.SetContentView(textPad, textPadLayoutParams);
NativeView = textPad;
}
}
bool _disposed;
protected override void Dispose(bool disposing)
{
if (disposing && !_disposed)
{
if (NativeView != null && ManageNativeControlLifetime)
{
RemoveView(NativeView);
NativeView.Dispose();
NativeView = null;
}
_disposed = true;
}
base.Dispose(disposing);
}
}
Now I've got another problem cause I have no more AppBar when this view is rendered on Android. But that is out of context. So thank you #YorkShen-MSFT for helping.

Related

How to set click listener to this Button

I cant get this to work I want the sign out Button on this preferences screen to have a ClickListener
This is how it looks like:
Here´s the code and the buttonView is always NULL:
class PreferencesFragment : PreferenceFragmentCompat() {
lateinit var activity: Context
private var prefs: SharedPreferences = BleApplication.getInstance().getDefaultSharedPreferences()
override fun onAttach(context: Context?) {
super.onAttach(context)
activity = requireActivity()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val buttonView = view.findViewById<View>(R.id.btn_sign_out)
if (buttonView != null) {
buttonView.setOnClickListener {
Toast.makeText(getActivity(), "You clicked me.", Toast.LENGTH_SHORT).show()
}
}
// Hide the divider
/* setDivider(ColorDrawable(Color.TRANSPARENT))
setDividerHeight(0)*/
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.app_prefs)
}
}
I also tried the kotlinx.android.synthetic but same problem there
Here´s the xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:layout="#layout/pref_category_text"
android:title="#string/pref_category_remote_battery_title">
<SwitchPreferenceCompat
android:key="#string/pref_key_category_remote_battery_switch"
android:title="#string/pref_category_remote_battery_switch_title"
android:summary="#string/pref_category_remote_battery_switch_summ"/>
</PreferenceCategory>
<PreferenceCategory
android:layout="#layout/pref_category_text"
android:title="#string/pref_category_sign_out_title">
<Preference
android:key="#string/pref_key_category_signed_out"
android:widgetLayout="#layout/pref_category_sign_out_button"
android:title="#string/pref_category_sign_out_button_title"
android:summary="#string/pref_category_sign_out_buttom_summ"/>
</PreferenceCategory>
</PreferenceScreen>
Here is the "#layout/pref_category_sign_out_button" layout
<?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="wrap_content"
android:orientation="vertical">
<Button
android:id="#+id/btn_sign_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/buttonshape"
android:text="#string/pref_category_sign_out_title" />
</LinearLayout>
Since your Fragment extends from PreferenceFragmentCompat, you should not try to set a View.OnClickListener but override PreferenceFragmentCompat.onPreferenceTreeClick() instead. According to the documentation, this method is ...
Called when a preference in the tree rooted at this PreferenceScreen has been clicked.
Code example in Java:
#Override
onPreferenceTreeClick(Preference preference){
if(preference.getKey().equals(getContext().getString(R.string.pref_key_category_signed_out))){
// user clicked signout "button"
// take appropriate actions
// return "true" to indicate you handled the click
return true;
}
return false;
}
Code example in Kotlin (I hope I can trust Android Studio :P)
override fun onPreferenceTreeClick(preferenceScreen: PreferenceScreen, preference: Preference): Boolean {
return if (preference.key == context.getString(R.string.pref_key_category_signed_out)) {
// user clicked signout "button"
// take appropriate actions
// return "true" to indicate you handled the click
true
} else false
}
This will enable you to catch click events for the Preference but not for the Button.
In order to do that as well, one can use a custom Preference and override onBindViewHolder(PreferenceViewHolder). Since PreferenceViewHolder - similar to RecyclerView.ViewHolder - has a field itemView which contains the inflated layout, here is a good opportunity to set our own View.OnClickListener.
SignOutPreference extends from TwoStatePreference (in the com.android.support:preference-v7 library) because replacing the CheckBox widget with your custom Button requires only to set the android:widgetLayout attribute, just like you do in your code snippet:
<PreferenceCategory
android:layout="#layout/pref_category_text"
android:title="#string/pref_category_sign_out_title">
<your.package.name.SignOutPreference
android:key="#string/pref_key_category_signed_out"
android:widgetLayout="#layout/pref_category_sign_out_button"
android:title="#string/pref_category_sign_out_button_title"
android:summary="#string/pref_category_sign_out_buttom_summ"/>
</PreferenceCategory>
SignOutPreference.java
public class SignOutPreference extends TwoStatePreference {
public SignOutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public SignOutPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SignOutPreference(Context context) {
super(context);
}
#Override
public void onBindViewHolder(final PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
Button button = holder.itemView.findViewById(R.id.btn_sign_out);
if(button != null){
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(holder.itemView.getContext(), "CLICKED!", Toast.LENGTH_SHORT).show();
}
});
}
}
}
As #0X0nosugar mentioned you can use the onPreferenceTreeClicked method to handle all clicks in a convenient way like this:
#Override
onPreferenceTreeClick(Preference preference){
if ((preference.getKey().equals(getContext().getString(R.string.pref_key_category_signed_out))){
// user clicked signout "button"
// take appropriate actions
// return "true" to indicate you handled the click
return true;
}
return false;
}
The problem when using a custom button via widgetLayout is that when the button click is not a Preference click, so the handler doesn't catch it. One way to circumvent this problem is to just disable the built-in click for the button, like this:
<Button
android:id="#+id/btn_sign_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/buttonshape"
android:text="#string/pref_category_sign_out_title"
android:clickable="false"/>
See the last line. This way you don't need to create an extra class just for the button and you can easily access whatever methods and variables you have in your PreferencesFragment class.
I'm sure there's a better way to somehow trigger the Preference click from the button click, but at least this works as pretty well.
I was looking for a simple answer and found a way. Just set the onClick attribute for the button in the xml file, and implement the method in the parent activity of the preference fragment. (It's important to implement it in the Activity, not in the Preference Fragment. Or else it will give you crashes)
I wanted to make my onClick method to work only when the button is touched(clicked), and not respond to clicks in the area outside of the button. So far this is the only way that works just like I wanted it to.
My code is in Kotlin, but the logic is simple so it won't be hard to write it in java.
This is my button.xml used for the preference's widgetLayout. Look at how I set the android:onClick= attribute.
<?xml version="1.0" encoding="utf-8"?>
<Button
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/btn"
android:text="reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/rippleEffect" //my custom ripple effect
android:onClick="onClickMethod">
</Button>
Then I implemented the onClickMethod in the parent activity of the preference fragment. This callback method should be public, and have View as input parameter.
(For more info read this -> How exactly does the android:onClick XML attribute differ from setOnClickListener? )
fun onClickMethod(view: View) {
//do something
}
Below is my preference.xml.
<Preference
android:key="pref_key"
android:title="Reset"
android:summary="summary.."
app:iconSpaceReserved="false"
android:widgetLayout="#layout/button"/>
Also I tried to set the ClickListener programmatically, but the only way that worked without errors was when I set the button's ClickListener inside the PreferenceClickListener. This only worked half way, since I need to touch(click) the preference item first to init the button's ClickListener.
val view = findPreference<androidx.preference.Preference>("pref_key")
var isFirst = true
view?.setOnPreferenceClickListener {
if (isFirst) {
btn.setOnClickListener {
Toast.makeText(requireContext(), "button clicked!", Toast.LENGTH_SHORT).show()
}
isFirst = false
it.summary = "unlocked"
} else {
isFirst = true
it.summary = "locked - tap to unlock"
}
true
}
Anyway this answer is working well for me, but I'm still looking for a way to use my preference key since this method does not fully uses the preference attribute, but just as a layout. But for now I hope this is helpful for those who want to use buttons in android preference.

How do I style the items list in a Xamarin Picker (in Android)

I have a Xamarin Android application that uses a Picker to select from a list of values. I have been changing the style of the application, but run into problems with the Picker.
Although I can set the TextColor, I could not set the colour of the placeholder text.
After searching SO for help, I've implemented a custom renderer, and I now have the text and placeholder showing in the correct text. However, previously when I touched the placeholder text, the child dialog appeared and displayed all the items, allowing the user to select one. Now I have the custom renderer implemented, the child dialog only shows the top two items, and the user has to scroll through them before hitting OK.
I have two questions:
At a minimum, how can I get the child dialog displaying the full list again?
Is it possible to set the background and text colour for the items list dialog?
The XAML looks like this:
<c:CustomPicker x:Name="DivisionList" Title="{x:Static prop:Resources.PickerDivision}"
SelectedIndexChanged="DivisionList_SelectedIndexChanged">
<Picker.Behaviors>
<b:RequiredPickerValidator x:Name="DivValidator" IsValid="{Binding Path=BindingContext.IsDivisionValid, Mode=OneWayToSource, Source={x:Reference contentPage}}" />
</Picker.Behaviors>
</c:CustomPicker>
The CustomPicker class is as follows:
namespace <myapp>.Portable.Controls
{
public class CustomPicker : Picker
{
public Color PlaceholderColour
{
get { return (Color)App.Current.Resources["PlaceholderTextColour"]; }
}
public Color TextColour
{
get { return (Color)App.Current.Resources["LabelTextColour"]; }
}
public Color BackgroundColour
{
get { return (Color)App.Current.Resources["PaneBackgroundColour"]; }
}
}
}
And the customer renderer is this:
[assembly: ExportRendererAttribute(typeof(CustomPicker), typeof(CustomPickerRenderer))]
namespace <myapp>.Droid.Controls.Renderers
{
public class CustomPickerRenderer : PickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
Control?.SetPadding(20, 20, 20, 20);
if (e.OldElement != null || e.NewElement != null)
{
var customPicker = e.NewElement as CustomPicker;
Android.Graphics.Color phCol = customPicker.PlaceholderColour.ToAndroid();
Android.Graphics.Color textCol = customPicker.TextColour.ToAndroid();
Android.Graphics.Color bgCol = customPicker.BackgroundColour.ToAndroid();
Control.SetBackgroundColor(bgCol);
Control.SetHintTextColor(phCol);
Control.SetTextColor(textCol);
}
}
}
}
Many thanks in advance!
Picker popup before custom renderer:
Picker popup after custom renderer:
There appear to be 2 picker renderers.
Xamarin.Forms.Platform.Android.PickerRenderer &
Xamarin.Forms.Platform.Android.AppCompat.PickerRenderer
Make sure you use the last one and your layout will be the same as before!!!
The source where i got my answer:
https://forums.xamarin.com/discussion/97150/how-to-write-a-custom-renderer-for-bindable-picker-to-change-its-font-attributes-family-size
Renderers (no real indication there are 2):
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/renderers
You can add Click event on Control, and then you can custom a dialog, in this dialog, you can custom anything what you want, background, item text color, etc.
In your CustomPickerRenderer, do like this:
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
Control?.SetPadding(20, 20, 20, 20);
if (e.OldElement != null || e.NewElement != null)
{
var customPicker = e.NewElement as CustomPicker;
...
// add click event
Control.Click += Control_Click;
}
}
You can show a Dialog, and custom it's layout, for example, add a ListView in its layout, so you will get the child dialog displaying the full list:
private void Control_Click(object sender, EventArgs e)
{
Toast.MakeText(Xamarin.Forms.Forms.Context,"111111",ToastLength.Short).Show();
dialog = new Dialog(Forms.Context);
dialog.SetContentView(Resource.Layout.dialog);
Android.Widget.Button b1 = (Android.Widget.Button)dialog.FindViewById(Resource.Id.button1);
Android.Widget.Button b2 = (Android.Widget.Button)dialog.FindViewById(Resource.Id.button2);
Android.Widget.ListView listView = (Android.Widget.ListView)dialog.FindViewById(Resource.Id.lv);
listView.Adapter=new ArrayAdapter<String>(Forms.Context, Android.Resource.Layout.SimpleExpandableListItem1,
new List<String>() { "111","222","333", "444", "555", "666", "777", "888", "999", });
b1.Click += B1_Click;
b2.Click += B2_Click;
dialog.Show();
}
Below is dialog layout, you can set the background and text color for the items list dialog:
<?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:text="123456"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ListView
android:id="#+id/lv"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cancel" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ok" />
</LinearLayout>
</LinearLayout>
To remove the lines and revert back to the default Picker View without modifying your custom renderer just extend this class
Xamarin.Forms.Platform.Android.AppCompat.PickerRenderer
It should look a little like this;
public class CustomPickerRenderer : Xamarin.Forms.Platform.Android.AppCompat.PickerRenderer
{
[logic]
}
instead of
public class CustomPickerRenderer : PickerRenderer
{
[logic]
}
It seems by default you extend Xamarin.Forms.Platform.Android and Class seems to be adding the different picker view.

Android- Unconventional Keyboard, change text on a key

I'm trying to develop an unconventional keyboard, I need to change the text in a key, depending on the combination of other keys pressed.
Like Candidates View but inside the keyboard, in one key.
I wonder if I can change the text on a key , or if I can use a different layout to the Keyboard XML.
my keyboard is based on the Soft Keyboard app example and can be referred to at this link .
You can use the Tags for your View, so you can use the setTag/getTag mechanism when pressing it..
private final View.OnClickListener myListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (v.getTag() != null) {
if (v.getTag() instanceof String) {
getCurrentInputConnection().commitText((String) v.getTag(), 1);
} else {
Log.v(TAG, "(v.getTag() instanceof String) == false");
}
} else {
Log.e(TAG, "(v.getTag() != null) == false");
}
}
};
And the XML example of a key:
<Button
android:id="#+id/aButton_N"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:tag="n"
android:text="n"
android:onClick="myListener"
android:textAllCaps="false" />
An example of alteration:
Button myBt = (Button) findViewWithTag("n");
myBt.setTag("N");
For all other purposes, remember that the "KeyboardView" is a facilitator. In your View, you can add any child of a View, so Buttons, ImageViews etc will work.
If you need the "image" (the Bitmap) that the KeyboardView uses, then you can "redrawn" (by overriding the onDrawn() function) of your keyboard, and make any alteration needed for keys or its layout...
Do note, that Keyboard, Keyboard.Key, etc. are all facilitators, for speed, low memory usage, and draw "easiness", by altering the layout, you can use anything with it, so ScrollViews / LinearLayouts / Framelayouts can greatly simplify your program/Views, but they use a bit more memory, check if your program can make this choice.
Edit:
A Button object, has several attributes. Its displaying text is a String, use button.getText().toString() to fetch it. A Tag, is a "pointer" to an Object of any kind.... if you want to writte the current text of the button, and use another related system, you can use the following:
private final View.OnClickListener myListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
Button thisButton = (Button) v;
getCurrentInputConnection().commitText(thisButton.getText().toString(), 1);
}
};
And to change the "content" of the button:
Button aButton = (Button) findViewById(YOUR.ID.HERE);
aButton.setText("a new long string to replace the old value.");
And sorry for bad english, if something is hard to understand, please, let me know
You can get all keys like that
List<Keyboard.Key> keys = mKeyboardView.getKeyboard().getKeys();
By proper event you can change key's icon or label, check Keyboard.Key.
You can try using the android:keyboardMode available as part of Keyboard.Row, it selectively displays row based on the mode you are in. So you can create multiple modes with different rows (keys with different values and functions).
Here is the link for reference.

How to disable user interaction while ProgressBar is visible in android?

I am using a custom ProgressBar. Now while a task is going on, I am showing the progress bar, but user can still interact with the views and controls.
How do I disable the user interaction on whole view just like a ProgressDialog does , when it is visible.
Do I need to use a transparent view on top of main view and show the progress bar on that view and hide that view once a task is completed.
Or just get the id of my parentView and set it disabled ? But then I won't be able to dim the background, just like what happens when a dialog appears on the view/Activity/Fragment. Right?
I just want to know the way to disallow the user from any interaction while the progressbar is visible.
Thanks
Your question: How to disable the user interaction while ProgressBar is visible in android?
To disable the user interaction you just need to add the following code
getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
To get user interaction back you just need to add the following code
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
Here is an example:
Note:I am giving you just an example to show how to disable or retain user interaction
Add a progress bar in your xml.Something like this
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/progressBar"
android:visibility="gone"/>
In MainActivity when a button pressed you show the progressbar and disable the user interaction.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = (ImageView) findViewById(R.id.imageView);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
mImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mProgressBar.setVisibility(View.VISIBLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}
});
}
And when user backPressed you remove the progressbar again retain the user interaction.Something like this
#Override
public void onBackPressed() {
super.onBackPressed();
mProgressBar.setVisibility(View.GONE);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}
If you want to add a feature of disable and greyed out display, you need to add in your xml layout file a linear layout that fills the parent. Set its background to #B0000000 and its visibilty to GONE. Then programmatically set its visibility to VISIBLE.
Hope this help!
I have fixed this issue by adding root layout to the ProgressBar.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:clickable="true"
android:gravity="center"
android:visibility="gone"
android:id="#+id/progress">
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true"
android:indeterminateTintMode="src_atop"
android:indeterminateTint="#color/primary"/>
</LinearLayout>
Made the root layout clickable
android:clickable="true"
NOTE: In my main view, I had RelativeLayout as root and have added above-mentioned code inside the root layout at the last position (last child).
Hope this helps!!
just set:
android:clickable="true"
in your xml
<ProgressBar...
Only this makes magic!
To extend (pun intended) on the accepted Answer :
When you use kotlin you can use extension functions. That way you have a quick and nice looking method for blocking and unblocking UI.
fun AppCompatActivity.blockInput() {
window.setFlags(
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
}
fun AppCompatActivity.unblockInput() {
window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
}
fun AppCompatActivity.blockInputForTask(task: () -> Unit) {
blockInput()
task.invoke()
unblockInput()
}
You can use the blocking and unblocking functions in your activity. Also, you can add more functionality like showing a Toast or something.
When using it in a custom view or any other view, you can simply cast the context to activity and use the functions.
Use blockInputForTask to surround simple linear tasks and blockInputand unblockInput when they are needed in different scopes.
You can use blockInputForTask like this:
blockInputForTask {
// Your lines of code
// Can be multiple lines
}
Use document default method progressbar.setCancelable(false)
Make a dialog with transparent background. The issue with getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); is that when app will go in background and come back user will be able to interact with UI components, a lot more handling. So for blocking UI make a transparent dialog and if you want to set time for hide/show. Do this in a runnable thread. So the solution will be
public class TransparentDialogHelper {
private Dialog overlayDialog;
#Inject
public TransparentDialogHelper() {
}
public void showDialog(Context context) {
if (AcmaUtility.isContextFinishing(context)) {
return;
}
if (overlayDialog == null) {
overlayDialog = new Dialog(context, android.R.style.Theme_Panel);
overlayDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED);
}
overlayDialog.show();
}
public void hideDialog() {
if (overlayDialog == null || AcmaUtility.isContextFinishing(overlayDialog.getContext())) {
return;
}
overlayDialog.cancel();
}
}
-------- Timer
Handler handler = new Handler();
handler.postDelayed( () -> {
view.hideProgress();
}, 2000);
Make your parent layout as Relative Layout & add this :
<RelativeLayout ... >
<other layout elements over which prog bar will appear>
<RelativeLayout android:id="#+id/rl_progress_bar"
android:layout_width="match_parent" android:clickable="true"
android:layout_height="match_parent" >
<ProgressBar android:id="#+id/pb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminateOnly="true"
style="#android:style/Widget.DeviceDefault.ProgressBar"
android:theme="#style/AppTheme.MyProgressBar"
/>
</RelativeLayout>
If you have floating buttons in your UI, they still grab all the focus & remain clickable when the progress bar is visible. for this use : (when your prog bar is visible & re-enable them when you make your prog bar invisible/gone)
fb.setEnabled(false);

ListView top highlight on scrolling

The border displays a default color (that's orange on my Nexus S) while scrolling a ListView to the limit. How to change that color?
I really don't know how to explain it. Just look at this picture:
So, how to change the highlight color when the ListView scrolling to the border? using themes or styles
The solution is to use setOverscrollFooter(null) and setOverscrollHeader(null).
The documentation is here !
You can also set it directly in the XML :
<ListView android:overScrollMode="never" />
Or specify the footer and the header :
<ListView
android:overscrollHeader="#null"
android:overscrollFooter="#null" />
N.B. : There is also a property fadingEdge that may interest you.
"Overscroll" methodes are supported starting API level 9
Finally I found the solution.
setOverscrollFooter(null) and setOverscrollHeader(null) does not work. At least on 2.3.*. Setting attributes from *.xml doesn't help too.
setOverScrollMode(View.OVER_SCROLL_NEVER) causes glitchy scrolling. At least on 2.3.*.
The only solution that really works involves the use of Java Reflection.
It works even with ugly custom Samsung listviews with bounce overscroll effect.
Here is a snippet:
#Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
//onOverScrolled method must be overrided, or we will see the background of the listview when overscroll fast.
}
private void removeOverscrollEffect() {
try {
Class<?> superClass = getClass().getSuperclass().getSuperclass();
Field field = superClass.getDeclaredField("mEdgeGlowTop");
field.setAccessible(true);
Object edgeGlowTop = field.get(this);
if (edgeGlowTop != null) {
Class<? extends Object> edgeClass = edgeGlowTop.getClass();
Field edgeDrawable = edgeClass.getDeclaredField("mEdge");
edgeDrawable.setAccessible(true);
edgeDrawable.set(edgeGlowTop, new ColorDrawable(Color.TRANSPARENT));
Field glowDrawable = edgeClass.getDeclaredField("mGlow");
glowDrawable.setAccessible(true);
glowDrawable.set(edgeGlowTop, new ColorDrawable(Color.TRANSPARENT));
field.set(this, edgeGlowTop);
}
Field fieldBottom = superClass.getDeclaredField("mEdgeGlowBottom");
fieldBottom.setAccessible(true);
Object edgeGlowBottom = fieldBottom.get(this);
if (edgeGlowBottom != null) {
Class<? extends Object> edgeClassBottom = edgeGlowBottom.getClass();
Field edgeDrawableBottom = edgeClassBottom.getDeclaredField("mEdge");
edgeDrawableBottom.setAccessible(true);
edgeDrawableBottom.set(edgeGlowBottom, new ColorDrawable(Color.TRANSPARENT));
Field glowDrawableBottom = edgeClassBottom.getDeclaredField("mGlow");
glowDrawableBottom.setAccessible(true);
glowDrawableBottom.set(edgeGlowBottom, new ColorDrawable(Color.TRANSPARENT));
fieldBottom.set(this, edgeGlowBottom);
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
I hope this helps.
Here is a nice article on ListView Backgrounds Optimization.
To fix this issue, all you have to do is either disable the cache color hint optimization, if you use a non-solid color background, or set the hint to the appropriate solid color value. You can do this from code (see setCacheColorHint(int)) or preferably from XML, by using the android:cacheColorHint attribute. To disable the optimization, simply use the transparent color #00000000. The following screenshot shows a list with android:cacheColorHint="#00000000"
Use it in XML file--
<ListView ---
android:fadingEdge="none"
---</ListView>
EDITED:
Using fading edges may introduce noticeable performance degradations and should be used only when required by the application's visual design. To request fading edges with API level 14 and above, use the android:requiresFadingEdge attribute instead.
Check this API link
I used kord's answer until it stopped working in Lollipop, so I changed into this:
try {
Class<?> superClass = getClass().getSuperclass().getSuperclass();
Field field = superClass.getDeclaredField("mEdgeGlowTop");
field.setAccessible(true);
field.set(this, new NoEdgeEffect(getContext()));
Field fieldBottom = superClass.getDeclaredField("mEdgeGlowBottom");
fieldBottom.setAccessible(true);
fieldBottom.set(this, new NoEdgeEffect(getContext()));
} catch (Exception e) {
}
class NoEdgeEffect extends EdgeEffect
{
public NoEdgeEffect(Context context) {
super(context);
}
public boolean draw(Canvas canvas) {
// Do nothing
return false;
}
}
you can use android:listSelector="#002234".
In above value can be any color code that you can find on internet easily.

Categories

Resources