Keyboard hides the input field in WebView - android

I have a layout:
<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"
tools:context=".StreamActivity"
android:orientation="vertical">
<FrameLayout
android:id="#+id/streamLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<SurfaceView
android:id="#+id/surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start">
</SurfaceView>
</FrameLayout>
<WebView
android:id="#+id/web_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="start"/>
FrameLayout for a video player. WebView is a chat.
When I begin to type text into the chat, the keyboard hides input field.
I tried experimenting with android: windowSoftInputMode, but it does not work for me. The problem with chat, it is written on Angular and resizing WebView does not change the size of the chat, it must be done manually like webView.loadUrl("javascript: $('section.content-window:first').css('height', '" + finalSize + "px');");
I have a listener that is triggered when the keyboard is shown and hidden.
I would like to manually change the size of the chat when the keyboard is showed, but it does not work well because when I change the size of chat it loses focus and the keyboard hidden.
I also thought to put the content of the Activity in ScrollView. If override OnTouchListener this will lock scroll by user, but I will still be able to scroll from code. So, I wanted if the keyboard is shown then scroll content of the Activity so it would be visible the entire chat. I think this will work, but not good because it will lock the scrolling in WebView.
Advise how to fix this problem with the keyboard and chat?

I found a solution:
<ScrollView ...
<LinearLayout ...
...
<com.package.TouchyWebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/webView"/>
...
</LinearLayout>
</ScrollView>
and do like this
package com.mypackage.common.custom.android.widgets
public class TouchyWebView extends WebView {
public TouchyWebView(Context context) {
super(context);
}
public TouchyWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TouchyWebView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onTouchEvent(MotionEvent event){
requestDisallowInterceptTouchEvent(true);
return super.onTouchEvent(event);
}
}
and
scrollView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
and to scroll I use code like this
final View activityRootView = findViewById(R.id.main_layout);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
boolean isOpen = false;
#Override
public void onGlobalLayout() {
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
if (!isOpen) {
scrollView.scrollBy(0, heightDiff);
isOpen = true;
}
} else {
if (isOpen) {
scrollView.scrollBy(0, -heightDiff);
isOpen = false;
}
}
}
});

Related

ViewPager : swipe only from a particular view

I have a created a custom viewpager, and i would like the user to swipe and change pages only from a part of the screen.
Every page has a linearlayout at the bottom with some text, and i would like to swipe only from there, not from the hole screen.
Is that possible?
This is my custom viewpager class:
public class MyViewPager extends ViewPager {
private boolean mSwipable = true;
public MyViewPager(Context context) {
super(context);
}
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mSwipable ? super.onInterceptTouchEvent(event) : false;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return mSwipable ? super.onTouchEvent(event) : false;
}
public boolean isSwipable() {
return mSwipable;
}
public void setSwipable(boolean swipable) {
mSwipable = swipable;
}
}
and this is the view of a page:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:clipChildren="false"
android:clipToPadding="false"
android:fillViewport="true"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:showIn="#layout/fragment_search">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/bottomLayout"
android:layout_marginBottom="#dimen/bottom_bar_height">
//show some stuff here
</LinearLayout>
<LinearLayout
android:id="#+id/bottomMenu"
android:layout_width="match_parent"
android:layout_height="#dimen/bottom_bar_height"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom"
android:orientation="horizontal">
// this is my footer, where i need to change the pages by swipe
</LinearLayout>
</RelativeLayout>
Follow this question in order to be able to disable swiping at will How do disable paging by swiping with finger in ViewPager but still be able to swipe programmatically?
And combine that with your SliderAdapter in order to control which pages will be swipeable or not.
If you followed the link above, on your subclass of ViewPager, you can add some GestureDetector on onTouchEvent(MotionEvent event) and feed it with your event, like Android: How to handle right to left swipe gestures
By doing this, you will now have some onSwipeRight and manage there your logic, delegating to the adapter the question if the page should be swiped or not, or where to go next.

Hide keyboard in Scrollview when user touches (outside), scrolls or clicks (other views) in Android

This might look like a redundant and already answered query but I am stuck. I have browsed through previously shared plethora of responses but none of them turned out to be a holistic solution.
Here's how the main activity xml looks like:
<RelativeLayout >
<include
android:id="#+id/toolbar"
layout="#layout/toolbar" />
<ScrollView >
<LinearLayout >
<android.support.v7.widget.CardView >
<LinearLayout >
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView >
</android.support.v7.widget.CardView>
</LinearLayout>
</ScrollView>
</RelativeLayout>
<android.support.design.widget.NavigationView />
The Problem
There is a edit text view in child linear layout of first card view. As of now, if I click on it, the keyboard pops up but doesn't hide when I click elsewhere or scroll the page or click any other view (there's a drop down).
What I plan to do is to hide the keyboard when,
1. I click anywhere outside the edit text view.
2. Scroll the page.
3. Interact with other views (the drop down).
Possible Solutions Tried
how to dismiss keyboard from editText when placed in scroll view - This one isn't working at all for me.
How to hide soft keyboard on android after clicking outside EditText? - There was one solution from #vida which worked partially, as in when clicked outside, the keyboard did dismiss. But then again, that's only a partial solution to what I am trying to achieve.
I would appreciate if someone could share a solution (or procedure) to sort this one out. Thanks!
You could try to override the Scroll Views onClickListener to call this method every time the user clicks anywhere on the app. If that doesnt work just set the outer relative layout to be clickable and everytime the layout is clicked you can call this hideKeyboard method!
public void hideKeyboard(View view) {
InputMethodManager im =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
im.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
First, extend EditText and add a snippet of code that helps dismiss the keyboard every time the EditText instances lose their focus
public class MyEditText extends AppCompatEditText {
public MyEditText(Context context) {
super(context);
setupEditText();
}
public MyEditText(Context context, AttributeSet attrs) {
super(context, attrs);
setupEditText();
}
public MyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setupEditText();
}
public void setupEditText() {
// Any time edit text instances lose their focus, dismiss the keyboard!
setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus && !(findFocus() instanceof MyEditText)) {
hideKeyboard(v);
} else {
showKeyboard(v);
}
}
});
}
public void hideKeyboard(View view) {
InputMethodManager inputMethodManager = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
public void showKeyboard(View view) {
InputMethodManager inputMethodManager = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(view, 0);
}
}
Then, set android:clickable="true" and android:focusableInTouchMode="true" in the child layout of your ScrollView!
Please note that, it should be the child layout of ScrollView, not ScrollView itself.
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusableInTouchMode="true"
android:orientation="vertical">
<MyEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</MyEditText>
</LinearLayout>
</ScrollView>
That should work!

sliding up panel - thin line sliding panel under google mapfragment

I made slidinguppanel using this repository https://github.com/dlukashev/AndroidSlidingUpPanel-foursquare-map-demo
However, it contains one bug which is not covered anywhere.
When I touch anywhere to expand panel (listview) works well, but while I'm trying to expand it by holding a top of a list view (blue line on screen2) panel hide under the map (framelayout) (screen3)
How it's even possible that this blue line hiding panel under mapfragment and rest of listview expand it well?
Any ideas why? Please give me just a hint how to fix it?
Please look at the screen:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<org.sothree.slidinguppanel.SlidingUpPanelLayout
android:id="#+id/slidingLayout"
android:gravity="bottom"
app:shadowHeight="0dp"
app:paralaxOffset="#dimen/paralax_offset"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<FrameLayout
android:gravity="top"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#+id/button">
<RelativeLayout
android:id="#+id/mapContainer"
android:layout_width="fill_parent"
android:layout_height="497dp"/>
</FrameLayout>
<RelativeLayout
android:id="#+id/slidingContainer"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#+id/button">
<View
android:id="#+id/transparentView"
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="#dimen/map_height"
android:layout_alignParentTop="true"/>
<ListView
android:id="#+id/listView1"
android:cacheColorHint="#android:color/white"
android:drawSelectorOnTop="true"
android:dividerHeight="#dimen/divider_height"
android:divider="#android:color/darker_gray"
android:background="#android:color/transparent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#id/transparentView"
android:smoothScrollbar="true"
android:layout_marginTop="0dp"/>
</RelativeLayout>
</org.sothree.slidinguppanel.SlidingUpPanelLayout>
</RelativeLayout>
You need to override ListView
public class LockableListView extends ListView {
private boolean mScrollable = true;
public LockableListView(Context context) {
super(context);
}
public LockableListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LockableListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setScrollingEnabled(boolean enabled) {
mScrollable = enabled;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// if we can scroll pass the event to the superclass
if (mScrollable) {
return super.onTouchEvent(ev);
}
// only continue to handle the touch event if scrolling enabled
return mScrollable; // mScrollable is always false at this point
default:
return super.onTouchEvent(ev);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Don't do anything with intercepted touch events if
// we are not scrollable
if (!mScrollable) {
return false;
} else {
return super.onInterceptTouchEvent(ev);
}
}
}
Then put disable scrolling when needed
private void collapseMap() {
mSpaceView.setVisibility(View.VISIBLE);
mTransparentView.setVisibility(View.GONE);
if (mMap != null && mLocation != null) {
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(mLocation, 11f), 1000, null);
}
mListView.setScrollingEnabled(true);
}
private void expandMap() {
mSpaceView.setVisibility(View.GONE);
mTransparentView.setVisibility(View.INVISIBLE);
if (mMap != null) {
mMap.animateCamera(CameraUpdateFactory.zoomTo(14f), 1000, null);
}
mListView.setScrollingEnabled(false);
}
I pushed this changes to GitHub https://github.com/dlukashev/AndroidSlidingUpPanel-foursquare-map-demo/commit/7b869394e9d197e6f56a833804426577dcb8458a
Enjoy

Adding text hint to Android SwipeRefreshLayout

How do I add a hint on the top of a listView like "Pull down to refresh" which is contained in a swipeRefreshLayout from android.support.v4.
The pull down to refresh works but I want to add a text whenever the user pulls the listview slightly down.
EDIT 10/21/2014
If you update the support-v4 to the latest version (at least 21.0.0) you can use the built-in loading indicator!
I just came up with a simple, yet effective, solution.
The idea is to add a custom ViewGroup that grows its height when the SwipeRefreshLayout child gets pulled down. In this ViewGroup you will put everything you need for your hint (TextViews, ImageViews, ...).
I chose to extend a RelativeLayout because it makes easier to position your "hint" elements.
1) Create a custom widget as follows:
public class SwipeRefreshHintLayout extends RelativeLayout {
public SwipeRefreshHintLayout(Context context) {
super(context);
}
public SwipeRefreshHintLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SwipeRefreshHintLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setSwipeLayoutTarget(final SwipeRefreshLayout swipeRefreshLayout) {
final View swipeTarget = swipeRefreshLayout.getChildAt(0);
if (swipeTarget == null) {
return;
}
swipeTarget.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
private Rect oldBounds = new Rect(), newBounds = new Rect();
#Override
public boolean onPreDraw() {
newBounds.set(swipeTarget.getLeft(), swipeRefreshLayout.getTop(), swipeTarget.getRight(), swipeTarget.getTop());
if (!oldBounds.equals(newBounds)){
getLayoutParams().height = newBounds.height();
requestLayout();
oldBounds.set(newBounds);
}
return true;
}
});
}
}
2) In your Fragment or Activity layout use this custom widget.
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.widget.SwipeRefreshHintLayout
android:id="#+id/swipe_hint"
android:layout_width="match_parent"
android:layout_height="0dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="#string/label_swipe_to_refresh"/>
<!-- Any other view positioned using RelativeLayout rules -->
</com.example.widget.SwipeRefreshHintLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
3) Then, in your Activity onCreate() or in your Fragment onCreateView(), put those lines:
mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipe_container);
mSwipeRefreshHintLayout = (SwipeRefreshHintLayout) rootView.findViewById(R.id.swipe_hint);
mSwipeRefreshHintLayout.setSwipeLayoutTarget(mSwipeRefreshLayout);
Done!

Scroll Webview inside a Scroll View

What I have:
Right now I have a Scroll view as a parent. Inside this scroll view, I am using a WebView that loads a URL and then shows text in it.
Here is my xml:
<ScrollView
android:id="#+id/parentScroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/Heading" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp" >
<WebView
android:id="#+id/webView"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#drawable/webview" />
</RelativeLayout>
</ScrollView>
What I want:
I want to scroll webView inside it. When I touch the webView, unfortunately the parent scroll view gets called.
I have to keep Parent scroll view also but besides this I want to scroll WebView content inside it when I touch on webView.
How can I do this?
Create a Custom Touch Intercepting Webview
CustomWebview.java
package com.mypackage.common.custom.android.widgets
public class CustomWebview extends WebView {
public CustomWebview(Context context) {
super(context);
}
public CustomWebview(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomWebview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onTouchEvent(MotionEvent event){
requestDisallowInterceptTouchEvent(true);
return super.onTouchEvent(event);
}
}
in layout.xml
<com.package.custom.widgets.CustomWebview
android:id="#+id/view_extra"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
According to Android design documents you should never put a scrolling container inside a scrolling container if they scroll the same direction. It's not meant to handle such a thing.
I had the same problem. You should set your webView Height equal as its content. for this do this:
add this lines to your onCreate method in Activity:
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
webView.loadUrl("javascript:MyApp.resize(document.body.getBoundingClientRect().height)");
super.onPageFinished(view, url);
}
});
webView.addJavascriptInterface(this, "MyApp");
and add this method to your activity:
#JavascriptInterface
public void resize(final float height) {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
webView.setLayoutParams(new LinearLayout.LayoutParams(getResources().getDisplayMetrics().widthPixels, (int) (height * getResources().getDisplayMetrics().density)));
}
});
}
Nested scrollable widgets are generally discouraged. It is a confusing user experience because it's easy to scroll the wrong thing. Say I intend to scroll the outer scroll region but I touched the inner region first and flinged really hard. Then the inner one will scroll and I'll be like huh? Why didn't it scroll?
Even if one scrolls horizontal and the other is vertical the gesture recognizers might confuse one for another so you get the same effect. It's a valid use case but it's still iffy and I'd avoid it. (IE: Humans don't perfectly swipe vertically and horizontally properly, it's usually with an angle.)
I would push to change the design to break out the scrollable areas. Ideally 1 scrollable item per page. Even propose one yourself and provide to the designer both experiences and see which one they choose.
To express how this will suck. Look at this example. This isn't a solution but just to show the experience.
<ScrollView
android:id="#+id/parentScroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/Heading" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent" android:orientation="vertical"
android:padding="5dp" >
<TextView
android:id="#+id/topText"
android:layout_width="match_parent"
android:layout_height="1000dp"
android:text="#string/lotsoftext" />
<WebView
android:id="#+id/webView"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#drawable/webview" />
<TextView
android:id="#+id/topText"
android:layout_width="match_parent"
android:layout_height="1000dp"
android:text="#string/lotsoftext" />
</RelativeLayout>

Categories

Resources