TextInputLayout hint overlap issue - android

I'm using TextInputLayout from Android Design Library to show label on EditText.
The problem is when I start activity with that EditText hint (label) text overlaps the actual text (for a second) and only then returns to its own place (at the top of the EditText).
To illustrate this issue I recorded a short sample video: https://youtu.be/gy0CzcYggxU
Here is my activity.xml:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:id="#+id/firstNameTextInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp">
<EditText
android:id="#+id/firstNameEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/first_name"
android:inputType="textCapWords"
android:textColor="#color/textPrimary"
android:textColorHint="#color/textSecondary"
android:textSize="16sp"
android:theme="#style/CustomEditText"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp">
<EditText
android:id="#+id/lastNameEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/last_name"
android:inputType="textCapWords"
android:textColor="#color/textPrimary"
android:textColorHint="#color/textSecondary"
android:textSize="16sp"
android:theme="#style/CustomEditText"/>
</android.support.design.widget.TextInputLayout>

I came up with a cheap workaround for this and another bug.
Subclass the TextInputLayout
See code for addView()
If you have text set in the text view when it is inflated it will set the hint to collapsed and prevent an animation. This code performs a workaround that will temporarily set text until the state is set during setup. As a bonus there is code that makes sure the hint gets drawn just in case there is only one layout pass.
public class TextInputLayout extends android.support.design.widget.TextInputLayout {
public TextInputLayout(Context context) {
super(context);
}
public TextInputLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#SuppressLint("DrawAllocation")
#Override
protected void onLayout(final boolean changed, final int left, final int top, final int right, final int bottom) {
if (ViewCompat.isLaidOut(this)) {
super.onLayout(changed, left, top, right, bottom);
} else {
// Workaround for this terrible logic where onLayout gets called before the view is flagged as laid out.
// The normal TextInputLayout is depending on isLaidOut when onLayout is called and failing the check which prevents initial drawing
// If there are multiple layout passes this doesn't get broken
post(new Runnable() {
#SuppressLint("WrongCall")
#Override
public void run() {
TextInputLayout.super.onLayout(changed, left, top, right, bottom);
}
});
}
}
#Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (child instanceof EditText) {
EditText editText = (EditText) child;
if (StringUtils.isEmpty(editText.getText().toString())) {
editText.setText(" "); // Set filler text so the initial state of the floating title is to be collapsed
super.addView(child, index, params);
editText.setText(""); // Set back to blank to cause the hint to animate in just in case the user sets text
// This prevents the hint from being drawn over text that is set programmatically before the state is determined
return;
}
}
super.addView(child, index, params);
}
}

The workaround that worked for me was to update activity like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
...
textInputLayout.setHintAnimationEnabled(false);
textInput.setText("sample");
textInputLayout.setHintAnimationEnabled(true);
...
}

Finally found the adequate explanation of the issue:
Well it turns out that there was a performance optimization added to
the framework in Android 4.0 which allowed your view hierarchy only
one single draw pass before the Activity animation was started. Once
the Activity animation has ended, your view hierarchy is drawn every
~16ms as you expect.
Read more: https://medium.com/#chrisbanes
TLDR: it is platform limitation and this behavior will occur on older versions (Marshmallow and lower).
On Nougat animation will run as expected without the lag.

You can set the hints programmatically with a small delay. This is not a ideal solution, but at least it looks better than overlapping hints.
new Handler().postDelayed(
new Runnable() {
#Override
public void run () {
textInputLayout.setHint("My hint");
}
}, 100
);

I think this may be fixed for compile 'com.android.support:design:23.0.1'

Daniel Ochoa noted a workaround in the comments which worked for me - set the initial state for the EditText with some text content (an empty string should do it). That'll force the hint's initial state to be up.
<android.support.design.widget.TextInputLayout
android:id="#+id/firstNameTextInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp">
<EditText
android:id="#+id/firstNameEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/first_name"
android:inputType="textCapWords"
android:textColor="#color/textPrimary"
android:textColorHint="#color/textSecondary"
android:textSize="16sp"
android:theme="#style/CustomEditText"
android:text=" "/>
</android.support.design.widget.TextInputLayout>

Related

ActionBar is scrolled up when using adjustpan

I have been stuck into this for quite some time. I am trying to develop a chat module. I have been stuck into this part where when the SoftInputKeyboard overlays the content of the RecyclerView. I have tried almost every combination of adjustResize and adjustPan with stateHidden and stateVisible with no success at all.
On Using adjustPan the ActionBar gets hidden along with 2-3 recyclerview items.
I have attached screenshots. Any help with be appreciated.
XML
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/light_grey"
android:fitsSystemWindows="true"
android:focusable="true"
android:focusableInTouchMode="true">
<LinearLayout
android:id="#+id/listFooter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<EditText
android:id="#+id/messageInput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#android:color/white"
android:hint="Write a message"
android:inputType="textMultiLine"
android:padding="16dp"
android:textSize="14sp" />
<ImageView
android:id="#+id/sendButton"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#color/colorPrimary"
android:contentDescription="#null"
android:padding="12dp"
android:src="#drawable/ic_send" />
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview_chat_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#+id/listFooter"
android:layout_marginTop="8dp" />
</RelativeLayout>
A ) AdjustResize
B ) With AdjustPan
The android:windowSoftInputMode does not scroll your content for you when the keyboard is shown/hidden.
The documentation says:
adjustResize - The activity's main window is always resized to make room for the soft keyboard on screen.
adjustPan - The activity's main window is not resized to make room for the soft keyboard. Rather, the contents of the window are
automatically panned so that the current focus is never obscured by
the keyboard and users can always see what they are typing. This is
generally less desirable than resizing, because the user may need to
close the soft keyboard to get at and interact with obscured parts of
the window.
Basically this means that adjustResize makes your rootview smaller and puts the softKeyboard below it. And adjustPan pushes the top half of the rootview out of the screen to make room for the the softKeyboard.
I would suggest using adjustResize because it wont push your Toolbar out of the screen. Then you would have to scroll the content yourself. Its easier said than done obviously, but there are methods built in to do this.
First you would have to get the last visible item position in the recyclerview.
//class variable
private int lastVisiblePosition = 0;
#Override
protected void onCreate(Bundle savedInstanceState)
{
//...
recyclerview_chat_main.addOnScrollListener(new RecyclerView.OnScrollListener()
{
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
super.onScrolled(recyclerView, dx, dy);
lastVisiblePosition = ((LinearLayoutManager)recyclerView.getLayoutManager()).findLastVisibleItemPosition();
}
});
//...
}
Then you have to do is scroll to that item when the SoftKeyboard is shown, the issue with that is there is no built in way to get when the keyboard is shown, fortunately someone has already addressed that here: https://stackoverflow.com/a/25681196/2027232
using Jaap's answer we could add something like this:
//class variables
private ViewGroup rootLayout = null;
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = null;
#Override
protected void onCreate(Bundle savedInstanceState)
{
//...
ViewGroup rootLayout = (ViewGroup)findViewById(android.R.id.content);
keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getHeight();
if(heightDiff > contentViewTop)
{
recyclerview_chat_main.getLayoutManager().scrollToPosition(lastVisiblePosition);
}
}
};
rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);
//...
}
And lastly dont forget to remove the global listener when the activity gets destroyed:
#Override
protected void onDestroy()
{
super.onDestroy();
if(rootLayout != null && keyboardLayoutListener != null)
rootLayout.getViewTreeObserver().removeOnGlobalLayoutListener(keyboardLayoutListener);
}
The accepted answer didn't work for me.
This works:
In your activity tag in manifest, add android:windowSoftInputMode="adjustResize".
In onCreate method of your activity add this:
View.OnLayoutChangeListener layoutChangeListener = new View.OnLayoutChangeListener()
{
#Override
public void onLayoutChange(View v, int left, final int top, int right, final int bottom,
int oldLeft, final int oldTop, int oldRight, final int oldBottom)
{
if (oldBottom != 0)
{
//when softkeyboard opens, the height of layout get's small, and when softkeyboa
//-rd closes the height grows back(gets larger).We can find the change of height
// by doing oldBotton - bottom, and the result of subtraction is how much we nee
//-d to scroll. Change of height is positive if keyboard is opened, and negative
//if it's closed.
int pixelsToScrollVertically = oldBottom - bottom;
recyclerView.scrollBy(0, pixelsToScrollVertically);
}
}
};
myAdapter = new MyAdapter();
recyclerView.setAdapter(myAdapter);
recyclerView.addOnLayoutChangeListener(layoutChangeListener);

HTML scrolling events in an Android WebView that's inside a ScrollView

I have a web page for testing purposes ( https://storage.googleapis.com/htmltestingbucket/nested_scroll_helper.html ) that just prints a counter of the scroll event the html has caught in a fixed header
When the Android WebView is the only scroll-able element in the fragment everything is fine and the WebView sends the scroll events to the page
If I want to add native elements above and below the WebView then things get much more complex.
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:text="SOMETHING ABOVE THE WEBVIEW" />
<WebView
android:id="#+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:text="SOMETHING BELOW THE WEBVIEW" />
</LinearLayout>
</ScrollView>
I know it's not good to have a WebView inside a ScrollView but I have to provide a single scrolling experience with hybrid content and proper scrolling events in the html document.
I found plenty of questions on the matter but I was able to create a full end-to-end solution
Also, I know lint has an Offical check for that:
NestedScrolling
--------------- Summary: Nested scrolling widgets
Priority: 7 / 10 Severity: Warning Category: Correctness
A scrolling widget such as a ScrollView should not contain any nested
scrolling widgets since this has various usability issues
And yet, I can't implement the web view content in native so I need an alternative way to do that
To Keep Webview inside scrollview here you need to measure height of the webview and set it in layout params.
Here i have tried to give answer for the scrollable webview.
<ScrollView
android:layout_width="fill_parent" android:background="#FF744931"
android:layout_height="fill_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:ignore="WebViewLayout">
<TextView
android:id="#+id/txtVoiceSeachQuery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF0000"
android:textSize="26sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:text="SOMETHING ABOVE THE WEBVIEW" />
<com.example.ScrollableWebView
android:id="#+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:isWebViewInsideScroll="true" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:text="SOMETHING BELOW THE WEBVIEW" />
</LinearLayout>
</ScrollView>
res/values/attrs.xml
To add attribute for the Custom Control
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ScrollableWebView">
<attr name="isWebViewInsideScroll" format="boolean"></attr>
</declare-styleable>
</resources>
ScrollableWebView
public class ScrollableWebView extends WebView {
private boolean webViewInsideScroll = true;
public static final String RESOURCE_NAMESPACE = "http://schemas.android.com/apk/res-auto";
public ScrollableWebView(Context context) {
super(context);
}
public ScrollableWebView(Context context, AttributeSet attrs) {
super(context, attrs);
setWebViewInsideScroll(attrs.getAttributeBooleanValue
(RESOURCE_NAMESPACE, "isWebViewInsideScroll", true));
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (isWebViewInsideScroll()) {
int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
setLayoutParams(params);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
public boolean isWebViewInsideScroll() {
return webViewInsideScroll;
}
public void setWebViewInsideScroll(boolean webViewInsideScroll) {
this.webViewInsideScroll = webViewInsideScroll;
}
}
To fetch attribute value you can also use Stylable but here i have done without using it.
ScrollableWebView webview = (ScrollableWebView) findViewById(R.id.webview);
webview.loadUrl("https://storage.googleapis.com/htmltestingbucket/nested_scroll_helper.html");
Below is link of output
Shows Textview(any view) on top inside scrollview with webview
Show TextView(any view) on bottom inside scrollview with webview
If you dont want to create attribute file & add Custom attributes in res/values/attrs.xml than you can ignore that file & check this pastebin here i gave without any custom attribute like isWebViewInsideScroll. you can remove it from xml layout too.
Let me know if anything.
if you place you webview inside scrollview you will not get html scrolling effect, because your webview content will not scroll ( it will be placed full lengtth inside scrollview.)
To face your need to place elements above and below you can listen to webview scroll and use DragViewHelper or nineoldandroids to move header and footer, so user will think, they are single element (you dont need scrollview).
webView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
ViewHelper.setTranslationY(headerTextView, -event.getY());
}
});
public class ObservableWebView extends WebView {
private OnScrollChangeListener onScrollChangeListener;
public ObservableWebView(Context context) {
super(context);
}
public ObservableWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ObservableWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (onScrollChangeListener != null) {
onScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);
}
}
public void setOnScrollChangeListener(OnScrollChangeListener onScrollChangeListener) {
this.onScrollChangeListener = onScrollChangeListener;
}
public OnScrollChangeListener getOnScrollChangeListener() {
return onScrollChangeListener;
}
public interface OnScrollChangeListener {
/**
* Called when the scroll position of a view changes.
*
* #param v The view whose scroll position has changed.
* #param scrollX Current horizontal scroll origin.
* #param scrollY Current vertical scroll origin.
* #param oldScrollX Previous horizontal scroll origin.
* #param oldScrollY Previous vertical scroll origin.
*/
void onScrollChange(WebView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY);
}
}
This example should help you to hide header, i used nineoldandroid for it
It seems the most elegant way I could find to handle this is as following:
- Listen to the SrollView scrolls:You can use an ObservableScrollView or call setOnScrollChangeListener() from API level 23
- Calculate the scroll Y offset in pixels
- Call the WebView.evaluateJavascript()
- Pass it all the details of the scroll event
So the general concepts is passing:
"$(document).trigger('scroll');" as the first param evaluateJavascript
I'm still testing the details and working out the kinks but it Seems like the better way to go, I will try to edit this answer with more info as I solve this
If anyway has a better solution for I would like to hear it
I have the same issue recently and I found your posts here :)
I have a WebView nested in a ScrollVIew. And the page which I loaded into WebView need to call a JS function when it scroll to the end, but in scroll view , web page's window.onscroll = functionXXX() {} never get called.
Finally, I have to set a OnScrollListener to the ScrollView, and call my JS function manually by the code below
#Override
public void onScroll(int scrollY) {
if (!scrollView.canScrollVertically(scrollY)) {
mWebView.loadUrl("javascript:functionXXX();");
}
}
Maybe our situations are different, but I hope it will give u some inspiration :)
in fact it is not so good put an scrollable view into another. Try to use this:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
And
<WebView
android:id="#+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>

How to disable blinking of textview onClick

I have a TextView with an onClickListener().
When I click on the TextView it blinks.
How to disable this effect?
Here is the XML
<TextView
android:id="#+id/descriptionText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#color/background"
android:ellipsize="end"
android:paddingTop="4dp"
android:paddingLeft="8dp"
android:maxLines="3"
/>
I tried to remove android:ellipsize and android:maxLines tags - no effect.
And here is the code:
accountDescriptionTextView = (TextView) v.findViewById(R.id.descriptionText);
accountDescriptionTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (descriptionOpened)
{
// accountDescriptionTextView.setEllipsize(TextUtils.TruncateAt.END);
// accountDescriptionTextView.setMaxLines(3);
descriptionOpened = false;
}
else
{
// accountDescriptionTextView.setEllipsize(null);
// accountDescriptionTextView.setMaxLines(100);
descriptionOpened = true;
}
}
});
I need to have the commented functionality, but even when this lines are commented I still see how the textview blinks.
The text just disapears when i place my finger on the screen and apears when I take the finger away.
Android uses a selector to give different colors to widgets' pressed state.
If you don't want that behavior, you can use solid colors for android:textColor and android:textColorHighlight.
Check the TextView doc.

Disable soft keyboard on NumberPicker

I'm trying to deactivate the soft keyboard when using a NumberPicker to enter numerical values (for aesthetic reasons). This is my layout-xml-code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/linearLayout2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="30dp"
android:layout_marginTop="30dp" >
<NumberPicker
android:id="#+id/repetitionPicker"
android:layout_width="40dp"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="#string/repetitions_short_divider"
android:textAppearance="?android:attr/textAppearanceMedium" />
<NumberPicker
android:id="#+id/weightPicker"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:layout_marginLeft="40dp" />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="#string/pounds"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<Button
android:id="#+id/saveButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="#string/save" />
</LinearLayout>
And finally this is the code where I try to block the keyboard in the onCreate()-method:
// hide keyboard
View.OnClickListener disableKeyBoardListener = new View.OnClickListener() {
public void onClick(View v) {
((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))
.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
};
((EditText) weightPicker.getChildAt(1)).setInputType(InputType.TYPE_NULL);
((EditText) repetitionPicker.getChildAt(1)).setInputType(InputType.TYPE_NULL);
((EditText) weightPicker.getChildAt(1)).setOnClickListener(disableKeyBoardListener);
//((EditText) repetitionPicker.getChildAt(1)).setOnClickListener(disableKeyBoardListener);
//weightPicker.setOnClickListener(disableKeyBoardListener);
//repetitionPicker.setOnClickListener(disableKeyBoardListener);
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
Sadly, the soft keyboard still shows up when clicking on a NumberPicker. Any ideas?
Just found this and it works like a charm:
myNumberPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
You can also set this in XML:
android:descendantFocusability="blocksDescendants"
Xml version of Andrew Webber's answer
android:descendantFocusability="blocksDescendants"
Example
<NumberPicker
android:id="#+id/your_numberpicker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"/>
After reading through the com/android/internal/widget/NumberPicker.java source code i got to the following solution:
// Hide soft keyboard on NumberPickers by overwriting the OnFocusChangeListener
OnFocusChangeListener fcl = new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
// Do nothing to suppress keyboard
}
};
((EditText) numberPicker.getChildAt(1)).setOnFocusChangeListener(fcl);
// Suppress soft keyboard from the beginning
((EditText) numberPicker.getChildAt(1)).setInputType(InputType.TYPE_NULL);
Just enhanced the #MaxVogler 's ans (so if wannt vote this vote #MaxVogler too) and make it a robust hack. Also we dont need to call setOnFocusChangeListener and setInputType. Only setFocusable to false will do.
Below is a helper api to enable/disable the feature
public static void enableNumberPickerManualEditing(NumberPicker numPicker,
boolean enable) {
int childCount = numPicker.getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = numPicker.getChildAt(i);
if (childView instanceof EditText) {
EditText et = (EditText) childView;
et.setFocusable(enable);
return;
}
}
}
Here's another way to do it which enables the user still to edit a number if they want to - it just suppresses the soft keyboard initially. Use NumberPicker.setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS) to suppress the soft keyboard when the interface first shows as per answers above. Then get your dialog or activity to implement View.OnTouchListener, call setOnTouchListener(this) on your NumberPicker, and in your implementation of onTouch(View v,MotionEvent e) reset the numberpicker descendant focusability to its normal value, then return false.
Returning false means that the touch is still processed by the NumberPicker, which means that if the user taps the edit box the soft keyboard comes up. This happens to be exactly what I wanted faced with the same problem - having the soft keyboard come up with the dialog when it first shows is displeasing as it shifts the dialog up after it appears.
public class GetBufferDialog extends DialogFragment implements View.OnTouchListener {
after creating the Dialog in the onCreateDialog() method and finding the NumberPicker:
m_oldFocus = m_numberpicker.getDescendantFocusability();
m_numberpicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
m_numberpicker.setOnTouchListener(this);
and here's the OnTouch method:
public boolean onTouch(View v, MotionEvent event) {
m_numberpicker.setDescendantFocusability(m_oldFocus);
return false;
}
Working code
Programatically :
mp.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
XML:
android:descendantFocusability="blocksDescendants"
I don't know why it works, but setting OnClickListener which does nothing prevented keyboard from showing (Lollipop)
numberPicker.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
}
});
The simplest I found to work was :
numberPicker = (NumberPicker) myDialogView.findViewById(R.id.myViewId);
EditText numberPickerChild = (EditText) numberPicker.getChildAt(0);
numberPickerChild.setFocusable(false);
numberPickerChild.setInputType(InputType.TYPE_NULL);
If you only want to hide the software keyboard when loading the view with your number picker, but still want the users to be able to edit after the view loads, then you shouldn't block descendant focusability. Instead, just prevent the number picker from being the first focused item in your view.
See this answer for details.
Based on the above answer:
<!-- Dummy item to prevent Number Picker from receiving focus -->
<LinearLayout
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_width="0px"
android:layout_height="0px"/>
<!-- :nextFocusUp and :nextFocusLeft have been set to the id of this component
to prevent the dummy from receiving focus again -->
<NumberPicker
android:id="#+id/number_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:nextFocusUp="#id/number_picker"
android:nextFocusLeft="#id/number_picker"/>
/**
* set focus to top level window
* disposes descendant focus
* disposes softInput
* #param context - activity context
* #param enable - state of focus
* */
public static void topLevelFocus(Context context, boolean enable){
if(Activity.class.isAssignableFrom(context.getClass())){
ViewGroup tlView = (ViewGroup) ((Activity) context).getWindow().getDecorView();
if(tlView!=null){
tlView.setFocusable(enable);
tlView.setFocusableInTouchMode(enable);
tlView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
tlView.requestFocus();
}
}
}
* calling this:
will not block descendant focusability (numberpicker will be editable)
will hide soft input on create
before (processing input) getValue() will allow to get proper walue
This extension is nice to not forget how to do it and have readable code. It is little bit hiding implementation details, but in this case I believe it's acceptable:
fun NumberPicker.disableTextEditing(disable: Boolean) {
descendantFocusability = if (disable) FOCUS_BLOCK_DESCENDANTS else FOCUS_BEFORE_DESCENDANTS
}

View over Canvas Visibility issue

I have this layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.components.game.GameView
android:id="#+id/game_id"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<RelativeLayout
android:id="#+id/ChatLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="invisible"
android:focusable="true"
android:focusableInTouchMode="true" >
<Button
android:id="#+id/ChatCancelButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="X" />
<Button
android:id="#+id/ChatOkButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="OK" />
<EditText
android:id="#+id/ChatEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="#+id/ChatOkButton"
android:layout_toRightOf="#+id/ChatCancelButton"
android:maxLength="50"
android:singleLine="true" />
</RelativeLayout>
</RelativeLayout>
It's a RelativeLayout over a canvas. At start time it's invisible but when a user clicks a button the layout should become visible.
The problem is that it's not becoming visible. The layout is there but it's just not drawing it. If I press the position where the layout should appear it receives the event and opens the keyboard but it's not drawing the whole layout.
What is the problem?
If I set the RelativeLayout to visible at the beginning it works fine. it shows the layout and if I toggle between invisible and visible it works fine.
I made a workaround that almost always works.
I start the layout visible and than do that in the oncreate:
chatLayout.postDelayed(new Runnable() {
#Override
public void run() {
chatLayout.setVisibility(View.INVISIBLE);
}
}, 50);
But I don't like it and want to understand what's the problem.
The code:
It starts from a canvas button which send a message to a handler:
public void showInputLayout() {
Message.obtain(gameHandler, SHOW_INPUT_LAYOUT).sendToTarget();
}
In the handler:
case SHOW_INPUT_LAYOUT:
gameActivity.setChatVisibility(true);
break;
setChatVisibility:
public void setChatVisibility(boolean isVisible) {
int visible = isVisible ? View.VISIBLE : View.INVISIBLE;
chatLayout.setVisibility(visible);
if(isVisible){
chatEditText.setFocusable(true);
chatEditText.requestFocus();
}
}
Add a click listener to RelativeLayout and switch the visibility between GONE and VISIBLE. Try something like this:
int visibility = View.VISIBLE;
RelativeLayout layout = (RelativeLayout)findViewById(R.id.ChatLayout);
layout.setVisibility(visibility);
layout.setOnClickListener(new View.OnClickListener{
public void onClick(View v)
{
if(visibility == View.VISIBLE)
visibility = View.GONE;
else
visibility = View.VISIBLE;
v.setVisibility(visibility);
}
})
I ran into a similar issue recently, and for my case the problem was actually in the onDraw() method of the view underneath (should be com.components.game.GameView in your case). See if you can add calls to Canvas' getSaveCount(), save() and restoreToCount() in your drawing code, similar to this:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int saveCount = canvas.getSaveCount();
canvas.save();
// custom drawing code here ...
// use Region.Op.INTERSECT for adding clipping regions
canvas.restoreToCount(saveCount);
}
I believe what happened was that sometimes the framework set the clipping regions for the elements on top of our Canvas-drawing widget before our onDraw() method is called so we need to make sure that those regions are preserved.
Hope this helps.

Categories

Resources