Frame layout getting stuck when I hide soft input (Android) - android

I have an Android app (fullscreen, landscape), and I have two Views, one is a FrameLayout that's always visible, and one's a LinearLayout. I set the visibility on the LinearLayout to View.GONE right off the bat. When the user taps a button on the FrameLayout, I set visibility on the LinearLayout to view.VISIBLE, and use the InputMethodManager to show the soft input on an EditText in the LinearLayout. This results in both views being shifted up such that the EditText is on screen just above the keyboard. This is all fine.
The problem arises when I close the soft input. In the same callback, I set the visibility of the LinearLayout (with the EditText) to View.GONE, and what I'm seeing is the FrameLayout getting stuck on top of the screen (it doesn't shift back down). If I don't GONE-ize the LinearLayout, both Views shift back down just fine.
I can work around the issue by starting a timer to wait before setting the visibility to GONE, but I shouldn't have to do that; there should be some notification that I can receive consistently that will indicate when I can hide the LinearLayout.
Things I've tried: hideSoftInputFromWindow with a ResultReceiver (result receiver callback gets invoked pretty well immediately (i.e. before the keyboard actually disappears), view is GONEd too quickly, same problem), overriding onLayout for the LinearLayout (doesn't get invoked when the soft input moves the View up and down), overriding other View "on"s.
The other really annoying thing is this problem seems to be exclusive to my phone - an AT&T Samsung Galaxy SII Skyrocket running Android 4.0.4 (possibly with some Samsung modifications). I can't repro on a 4.0.3 simulator, nor can anyone else repro on other devices with other Android versions.
Any ideas?

You're going to have to write your own touch event handler. Here is an example that will slide the whole frameLayout view down when the soft input is hidden. As you can see, it is important to call the super handler and wrap our code in a test for textEdit widgets. If you want to get a look at the calculations, uncomment the debugging logger code.
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
View v = getCurrentFocus();
boolean ret = super.dispatchTouchEvent(event);
if (v instanceof EditText) {
View w = getCurrentFocus();
int scrcoords[] = new int[2];
w.getLocationOnScreen(scrcoords);
float x = event.getRawX() + w.getLeft() - scrcoords[0];
float y = event.getRawY() + w.getTop() - scrcoords[1];
// Log.d("Activity",
// "Touch event "+event.getRawX()+","+event.getRawY()+" "+x+","+y+" rect "+w.getLeft()+","+w.getTop()+","+w.getRight()+","+w.getBottom()+" coords "+scrcoords[0]+","+scrcoords[1]);
if (event.getAction() == MotionEvent.ACTION_UP
&& (x < w.getLeft() || x >= w.getRight() || y < w.getTop() || y > w
.getBottom())) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindow().getCurrentFocus()
.getWindowToken(), 0);
}
}
return ret;
}

Related

android - show keyboard fix bottom framelayout

I am developing an android application in which my mainActivity has 5 tabs at the bottom represented in FrameLayout. Each tab points to a particular fragment which gets loaded based on the tab clicked.
In my 1st tab, I've an EditText. When the edit text is focused, I need to show the keyboard by just moving the layout of ('EditText and Send button) "up" without pushing up the other components including the bottom tab frame layout. Similarly when hiding the keyboard, the 'EditText and Send btn' layout should sit just above the tab frame layout. Can someone please suggest me the solution for this?
(Assume the situation same as in Facebook comment. When we go inside a post to comment something, the layout ('Write Comment' ET and POST btn) will be pushed up without moving any other components including the bottom tabs ('News Feed', 'Requests', 'Messenger', 'Notifications', 'More'))
I don't want to make the frame layout Visibility.Gone/Visible as it ends up in an ugly animation when the keyboard slides up/down.
Use a LinearLayout whit no weightSum, and give your main View in your layout 0dp height, and weight 1
I have the exact same issue. The best workaround I could find (as alluded to in the original question) is below. But this is a marginal solution, and not a great one.
The problem with setting the weightSum is that if the scrollview containing the edit boxes is longer, the button will disappear. The best solution would allow the buttons to stay at bottom of page on normal view, but to be hidden during keyboard onscreen.
public boolean dispatchTouchEvent(MotionEvent event) {
View v = getCurrentFocus();
boolean ret = super.dispatchTouchEvent(event);
if (v instanceof EditText) {
View w = getCurrentFocus();
int scrcoords[] = new int[2];
w.getLocationOnScreen(scrcoords);
float x = event.getRawX() + w.getLeft() - scrcoords[0];
float y = event.getRawY() + w.getTop() - scrcoords[1];
if (event.getAction() == MotionEvent.ACTION_UP && (x < w.getLeft() || x >= w.getRight() || y < w.getTop() || y > w.getBottom()) ) {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindow().getCurrentFocus().getWindowToken(), 0);
layButton.setVisibility(View.VISIBLE);
}
else
{
layButton.setVisibility(View.GONE);
}
}
return ret;
}

How to detect View pager vertical swipe and send that event to the layout below ?

I have one view that has some vertical gestures like swipe down to move the view down etc. lets call this View rootView. Everything was good util I needed to add a view pager on top of it. Now the ViewPager is consuming all the touch events. The ViewPager is scrolling horizontally as it should, but consuming the vertical swipes too and doing nothing (not passing the event to the rootView). How to make both the Views listen to their corresponding touch events and send the rest to other. I tried creating a CustomViewPager and overriding its onTouchEvent in the hope of recognizing the swipe down first and return false in that case so that the rootview will get a chance to handle that touch event. But in the process of recognizing the gesture as down/up , the ViewPager is consuming ACTION_DOWN and ACTION_MOVE event which are needed by the rootView to process the amount of finger movement.
A solution that came to my mind is to add onTouchEvent on all the layout over the ViewPager, which recognize the Vertical vs horizontal and call the appropriate touchevent (of rootView vs ViewPager) , but in order to recognize the up/down/side gesture, the layout will consume some events which may be valueable to the rootView.
Another solution that comes to mind is to override the ontouchEvent of ViewPager and call the onTouchEvent of the rootView irrespective of the up/down/side movement. In this way both the ViewPager and rootView can use the event, but it is sometimes making the screen fluctuates.
How should I solve this problem ? I would appreciate some suggestions, and no need to provide the code, just a good way to solve this problem.
I've had to completely modify my original answer. My first solution was to subclass ViewPager and override onTouchEvent(), adding logic to switch between the CustomViewPager and the RelativeLayout below it. Returning false means the view ignores the event, and returning super.onTouchEvent(event) means the view consumes the event until the user lifts their finger. All the logic was in the ACTION_MOVE part of the event, but since you only get one chance to return a value (which is the first part of the event, ACTION_DOWN), you can't use any movement to determine whether to consume or ignore the event.
I finally got it to work by writing an entirely new class, starting with the ViewPager code and adding logic to recognize vertical gestures. When a vertical gesture occurs, code is run that acts on the RelativeLayout below it, instead of passing the touch event to it. Below is a code snippet from the onTouchEvent() of my class.
public class CustomViewPager extends ViewGroup {
// code from ViewPager class...
case MotionEvent.ACTION_MOVE:
if (!mIsBeingDragged) {
if (DEBUG) Log.v(TAG, "!mIsBeingDragged");
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float xDiff = Math.abs(x - mLastMotionX);
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float yDiff = Math.abs(y - mLastMotionY);
if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
if (xDiff > mTouchSlop && xDiff > yDiff) {
isVerticalMovement = false;
hasMovedHorizontally = true;
if (DEBUG) Log.v(TAG, "Starting drag!");
mIsBeingDragged = true;
requestParentDisallowInterceptTouchEvent(true);
mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :
mInitialMotionX - mTouchSlop;
mLastMotionY = y;
setScrollState(SCROLL_STATE_DRAGGING);
setScrollingCacheEnabled(true);
// Disallow Parent Intercept, just in case
ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
else {
if (yDiff > mTouchSlop) {
// VERTICAL SWIPE -- do stuff here...
}
}
}
}
Have you tried placing the rootView on top of the ViewPager?
That way, the ViewPager should still work although being under the rootView

Android : keyboard show/hide with multiple EditText

I have an activity with a listview and each row has several editText. It looks like a questionnaire.
I have for example the focus on one EditText, then I click on another EditText. At the moment, the keyboard disappear and reappear. It is very irritating.
I use that code to make the keyboard disppear when the user click anywhere else on the activity when he don't want to type. But if the user click on another editText, I would like the keyboard stay.
Someone has an idea if the keyboard is already shown, how to keep it open when I click on a EditText?
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
View w = getCurrentFocus();
if (w instanceof EditText) {
int scrcoords[] = new int[2];
w.getLocationOnScreen(scrcoords);
float x = event.getRawX() + w.getLeft() - scrcoords[0];
float y = event.getRawY() + w.getTop() - scrcoords[1];
if (event.getAction() == MotionEvent.ACTION_UP && (x < w.getLeft() || x >= w.getRight() || y < w.getTop() || y > w.getBottom()) ) {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindow().getCurrentFocus().getWindowToken(), 0);
}
}
return super.dispatchTouchEvent(event);
}
Make sure you're setting your android:inputType on your EditText declarations in XML. If this doesn't solve the issue by itself, you can specify next focuses using the View class's android:nextFocusUp, android:nextFocusDown, android:nextFocusRight, or android:nextFocusLeft to indicate the next EditText to move to.
Lastly, it could have something to do with how the ListView is handling focus. If that's the case, there's a related question here that would probably be worth looking at.
Would be helpful if you could share your code.
I think what is happening is that your listview is causing your edit text to lose focus when you tap another one on a different row.
In general, what you could do is to register on a focus change listener on your edit text and then every time your edit text loses focus you can run a function to display the keyboard again.
replyInputEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
showKeyboard(editText);
}
});
The showkeyboard function :
public void showKeyboard(EditText field){
field.setFocusable(true);
field.setFocusableInTouchMode(true);
field.requestFocus();
InputMethodManager imm = (InputMethodManager) activity.getSystemService(activity.getApplicationContext().INPUT_METHOD_SERVICE);
imm.showSoftInput(field, InputMethodManager.SHOW_IMPLICIT);
}
You could also register an ontouchup listener on the text fields and then calling the showKeyboard function.

AutoCompleteTextView always keeps focus

I have 2 AutoCompleteTextViews in an activity (LinearLayout) and several additional controls (radiogroups, buttons, etc). Somehow the AutoCompleteTextViews are never losing focus.
As Example:
The user clicks on an AutoCompleteTextView, the control gets the focus. So the cursor starts blinking, the autocomplete dropdown list and the keyboard is shown. This is fine.
However if the user now clicks on of the radio buttons (or another control), the cursor in the AutoCompleteTextView is still blinking and the keyboard is still shown.
How to make the focus disappear automatically?
EDIT: xml code
<AutoCompleteTextView
android:id="#+id/ediFrom"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:text="" />
Only solution that worked for me is to add this line
android:focusable="true"
android:focusableInTouchMode="true"
To parent of AutoCompleteTextView (like LinearLayout etc..)
have u tried with android:focusableInTouchMode="true" for each view
code snippet
<AutoCompleteTextView
android:id="#+id/ediFrom"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:focusableInTouchMode="true"
android:text="" />
http://android-developers.blogspot.in/2008/12/touch-mode.html
In order to avoid setting everything else focusable (which is painful if you happen to use the same text view in many other layouts), we opt to override the logic to intercept touch screen events at activity level instead:
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
View v = getCurrentFocus();
if (v instanceof EditText) {
int scrcoords[] = new int[2];
v.getLocationOnScreen(scrcoords);
// calculate the relative position of the clicking position against the position of the view
float x = event.getRawX() - scrcoords[0];
float y = event.getRawY() - scrcoords[1];
// check whether action is up and the clicking position is outside of the view
if (event.getAction() == MotionEvent.ACTION_UP
&& (x < 0 || x > v.getRight() - v.getLeft()
|| y < 0 || y > v.getBottom() - v.getTop())) {
if (v.getOnFocusChangeListener() != null) {
v.getOnFocusChangeListener().onFocusChange(v, false);
}
}
}
return super.dispatchTouchEvent(event);
}
If you put this logic in your base activity, any screen with an edit text now will fire onFocusChange when you tap anywhere outside it. By listening to onFocusChange you can clearFocus or requestFocus on another view. It's a hack more or less but at least you don't have to set focusable for any other items on many layouts.
See http://developer.android.com/reference/android/app/Activity.html#dispatchTouchEvent(android.view.MotionEvent)
You can use the method setOnDismissListener() to clear the focus whenever the dropdown is dismissed (value picked, clicked outside)
This Kotlin code works fine for me, you can easily rewrite it to java too:
materialAutoCompleteTextBox.setOnDismissListener {
materialAutoCompleteTextBox.clearFocus()
}

Floating List view in android?

Is it possible to create a list view in android that is not fixed to a particular position and user can move the list view on a gesture some what floating listview that can be moved anywhere on the screen?I tried finding it but could not find some link.Does any one have some idea about it?
You can achieve this by setting an OnTouchListener on the list view and overriding it's onTouch() method. In the onTouch() method you will have to handle the touch events like(ACTION_DOWN, ACTION_MOVE). Here is how you can handle touch event:
#Override
public boolean onTouch (View v, MotionEvent event)
{
switch(event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
start.set(event.getX(), event.getY()); //saving the initial x and y position
mode = DRAG;
break;
case MotionEvent.ACTION_MOVE:
if(mode == DRAG) {
float scrollByX = event.getX() - start.x; //computing the scroll in X
float scrollByY = event.getY() - start.y; //computing the scroll in Y
v.scrollBy((int)scrollByX/20, 0);
}
break;
}
return false;
}
When the user places the finger down on the screen the ACTION_DOWN event is triggered and when the user drags his finger on the screen ACTION_MOVE event is triggered. Hence a drag event is a combination of ACTION_DOWN and ACTION_MOVE.
Once you have the scrollX and scrollY value the you can move the view on the screen by passing these values to the scrollBy() method of the View class.
Here in the code there are two things to notice:
first is that I have passed 0 as the value of scrollY as it will prevent the list view to be dragged and scrolled simultaneously.
second is that the return value is false as you want the android to handle the scrolling of the list items
Here you can find the complete code for this
I would move the listview using the margins from the layout.
Outline:
Override OnTouch, and based on the event.getX() and event.getY(), update the margins of the listview as follows:
if the parent is a relative layout:
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)mListView.getLayoutParams();
lp.leftMargin = event.getX();
lp.topMargin = event.getY();
mListView.setLayoutParams(lp);
Add the dispatchTouchEvent to your activity so that events from the listview get passed up to onTouch:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
Hope this helps.
Although it's hard to maintain, AbsoluteLayout lets you specify a position in (x,y) coordinates. You would just declare your ListView inside a AbsoluteLayout, get a handle to it in java code where you intercept the clicks and then set new coordinates.
In the Android framework, the word "floating" is usually synonymous with new Window (such as a Dialog). It depends somewhat on what your end goal is, but if you want to create a ListView that floats over the rest of the content in your hierarchy, that you can easily hide and show, you should probably consider wrapping that view inside a PopupWindow, which has methods to allow you to easily show the window with showAsDropdown() and shotAtLocation() and also move it around while visible with update().
This is how the newer versions of the SDK create pop-up lists for Spinner and Menu

Categories

Resources