Adjust Scrollview height when keyboard is hidden/visible - android

I have an activity with bunch of controls (EditText, Spinner etc.), with one of the EditText's having a custom keyboard. Here is how my XML looks like
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/masterRelativeLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ScrollView
android:id="#+id/mainScrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true" >
<LinearLayout
android:id="#+id/childLinearLayout"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<!-- Many EditText's, Spinners here -->
</LinearLayout>
</ScrollView>
<android.inputmethodservice.KeyboardView
android:id="#+id/myKeyboardView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:keyPreviewLayout ="#layout/kbdpreview"
android:layout_alignParentBottom="true"
android:visibility="gone" />
</RelativeLayout>
Notice that the keyboard view is bottom-aligned, so that it shows up at the bottom of the screen.
The custom keyboard gets hidden and shown on the specific EditText as follows:
These methods are called when a specific EditText in the activity is touched.
private OnTouchListener m_onTouchListenerNotationText = new OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
if (v == m_notationText)
{
// Hide the default keyboard
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
m_notationText.onTouchEvent(event);
m_customKeyboard.showCustomKeyboard(v);
AdjustScrollView();
return true; // Done with the event
}
return false;
}
};
Whenever the custom keyboard appears on the screen, it hides some of the controls in my view. However, Android framework does not know that this keyboard is being shown so the scrollview does not adjust. The controls remain hidden behind the custom keyboard. Hence, I have implemented a callback in the custom keyboard which gets called when the keyboard is visible.
// This listener is in showCustomKeyboard(...) function
// Set a static variable m_height and tell the activity to adjust the scrollview after keyboard becomes VISIBLE
mKeyboardView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#SuppressLint("NewApi")
#SuppressWarnings("deprecation")
#Override
public void onGlobalLayout()
{
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
if (currentapiVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN)
{
mKeyboardView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
else
{
mKeyboardView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
m_height = mKeyboardView.getHeight();
Log.d(TAG, "Custom Keyboard now visible, height = " + m_height);
mHostActivity.AdjustScrollView();
}
});
// This listener is in hideCustomKeyboard(...) function
// Set a static variable m_height and tell the activity to adjust the scrollview after keyboard becomes HIDDEN
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#SuppressLint("NewApi")
#SuppressWarnings("deprecation")
#Override
public void onGlobalLayout()
{
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
if (currentapiVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN)
{
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
else
{
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
m_height = 0;
Log.d(TAG, "Custom Keyboard now hidden");
mHostActivity.AdjustScrollView();
}
});
Finally, the scrollview adjustment happens as follows:
public synchronized void AdjustScrollView()
{
RelativeLayout rl = (RelativeLayout) findViewById(R.id.masterRelativeLayout);
int masterHeight = rl.getHeight();
Log.d(TAG, "Scrollview master relative layout height = " + masterHeight);
LinearLayout ll = (LinearLayout) findViewById(R.id.childLinearLayout);
int childHeight = ll.getHeight();
Log.d(TAG, "Scrollview child linear layout height = " + childHeight);
ScrollView scrollView = (ScrollView) findViewById(R.id.mainScrollview);
if (masterHeight - childHeight - m_customKeyboard.m_height <= 0)
{
// Need to adjust scrollview's height
scrollView.getLayoutParams().height = masterHeight - m_customKeyboard.m_height;
Log.d(TAG, "Setting scrollview.layoutparams.height = " + (masterHeight - m_customKeyboard.m_height));
}
else if (m_customKeyboard.m_height == 0)
{
// Need to adjust scrollview's height to full screen
scrollView.getLayoutParams().height = childHeight;
Log.d(TAG, "Setting scrollview.layoutparams.height = " + childHeight);
}
}
AndroidManifest.xml has the following line for this activity:
android:windowSoftInputMode="stateAlwaysHidden|stateUnchanged|adjustResize"
This mostly works, with 2 problems:
Problem 1: When I press long press on the EditText which shows custom keyboard, sometimes there is a race condition whereby the default keyboard AND custom keyboard both show up momentarily, then the default keyboard goes away (because I am hiding it in m_onTouchListenerNotationText). But in this time, the calculation of AdjustScrollView gets messed up, because now, the master relative layout height is very small (total height - custom keyboard height - default keyboard height). So the scrollview height calculation above is wrong. The scrollview now gets confined to a very tiny area at the top of the screen, followed by white space, followed by my custom keyboard at the bottom. I worked around this problem by adding call to AdjustScrollView in touch listener for the EditText (so whenever this happens, the user can click on that tiny view and the touch listener will adjust the scrollview). This work-around is undesirable, because, then it makes the long-press useless (I want the user to be able to long-press to show the default system menu of cut/copy/paste etc.). The work-around of requiring an additional touch makes this system menu go away.
Problem 2: If the custom keyboard is hidden, The scrollview does not take the entire screen size again. The bottom part of the screen, which was occupied by the custom keyboard, remains blank and the scrollview is only in the top part of the screen.

The only solution I found is to use keyboard hide-show detection like in this example:
https://stackoverflow.com/a/7423586/1979882
The concept is to overwrite the background Layout to your custom and measure.
public class VLinearLayoutKeyboardListener extends LinearLayout {
public interface IKeyboardChanged {
void onKeyboardShown(int actualHeight, int proposedheight);
void onKeyboardHidden(int actualHeight, int proposedheight);
}
private ArrayList<IKeyboardChanged> keyboardListener = new ArrayList<IKeyboardChanged>();
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public VLinearLayoutKeyboardListener(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VLinearLayoutKeyboardListener(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VLinearLayoutKeyboardListener(Context context) {
super(context);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public VLinearLayoutKeyboardListener(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
// TODO Auto-generated constructor stub
}
public void addKeyboardStateChangedListener(IKeyboardChanged listener) {
keyboardListener.add(listener);
}
public void removeKeyboardStateChangedListener(IKeyboardChanged listener) {
keyboardListener.remove(listener);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
final int actualHeight = getHeight();
if (actualHeight > proposedheight) {
notifyKeyboardShown(actualHeight, proposedheight);
} else if (actualHeight < proposedheight) {
notifyKeyboardHidden(actualHeight, proposedheight);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void notifyKeyboardHidden(int actualHeight, int proposedheight) {
for (IKeyboardChanged listener : keyboardListener) {
listener.onKeyboardHidden(actualHeight, proposedheight);
}
}
private void notifyKeyboardShown(int actualHeight, int proposedheight) {
for (IKeyboardChanged listener : keyboardListener) {
listener.onKeyboardShown(actualHeight, proposedheight);
}
}
}
in an Activity
public class mAct extends Activity implements IKeyboardChanged {
private VLinearLayoutKeyboardListener vllkl;
onCreate(){
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
#Override
public void onDestroy() {
super.onDestroy();
vllkl.removeKeyboardStateChangedListener(this);
}
#Override
public void onKeyboardShown(int actualHeight, int proposedheight) {
//actualHeight - old value
//proposedheight - new value
Log.d(TAG,"onKeyboardShown(): [" + actualHeight + ", " + proposedheight + "]");
//here you can setup Views heights
}
#Override
public void onKeyboardHidden(int actualHeight, int proposedheight) {
//actualHeight - old value
//proposedheight - new value
Log.d(TAG,"onKeyboardHidden(): [" + actualHeight + ", " + proposedheight + "]");
//here you can setup Views heights
}
}
in XML
<?xml version="1.0" encoding="utf-8"?>
<your.package.name.VLinearLayoutKeyboardListener xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/black"
android:orientation="vertical"
android:id="#+id/root">
....
</your.package.name.VLinearLayoutKeyboardListener >

Related

Scale up Item in RecyclerView to overlaps 2 adjacent items Android

I'm using RecyclerView with GridLayoutManager. The goal I want to achieve is when I click on an item, It'll scale up and overlaps the adjacent items. Just like the picture below (in Android TV)
When the onClick event is triggered, I call
v.animate().scaleX(1.2f).scaleY(1.2f).setDuration(500).start();
But the result is below:
It can overlaps only items that has position lower than itself.
What should I do to overlaps all of the adjacent items. Thanks in advance.
EDIT
I already tried:
v.bringToFront();
or
(v.getParent()).bringChildToFront(v);
But both of them don't work.
According aga's answer, i use ViewCompat.setElevation(View view, float elevation) to support API prior to 21, It works like a charming.
static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View root) {
// bind views
// ...
// bind focus listener
root.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
// run scale animation and make it bigger
ViewCompat.setElevation(root, 1);
} else {
// run scale animation and make it smaller
ViewCompat.setElevation(root, 0);
}
}
});
}
}
The item layout file is very simple like below:
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
android:focusableInTouchMode="true"
android:background="#drawable/sl_live_item" />
1.Override the getChildDrawingOrder method.
#Override
protected int getChildDrawingOrder(int childCount, int i) {
View view = getLayoutManager().getFocusedChild();
if (null == view) {
return super.getChildDrawingOrder(childCount, i);
}
int position = indexOfChild(view);
if (position < 0) {
return super.getChildDrawingOrder(childCount, i);
}
if (i == childCount - 1) {
return position;
}
if (i == position) {
return childCount - 1;
}
return super.getChildDrawingOrder(childCount, i);
}
2.RecyclerView setChildrenDrawingOrderEnabled is true.
setChildrenDrawingOrderEnabled(true);
3.When item view has focus, invalidate its parent.
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
v.setScaleX(1.5f);
v.setScaleY(1.5f);
mRecyclerView.invalidate();
} else {
v.setScaleX(1.0f);
v.setScaleY(1.0f);
}
}
Try to invoke v.bringToFront() before running the animation.
If it doesn't work another variant is to use View#setElevation(float val) where val > 0. The drawback is that you'll need to set elevation back to 0 when you're deselecting the item.
val zoom = resources.getFraction(...) // Your zoom fraction in %
override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
val viewPropertyAnimator: ViewPropertyAnimator
val f: Float
if(gainFocus) {
tvAppName.visibility = VISIBLE
f = this.zoom;
viewPropertyAnimator = animate().z(2.0f).scaleX(this.zoom);
} else {
tvAppName.visibility = INVISIBLE
ivAppIcon.strokeWidth = 0f
f = 1.0f;
viewPropertyAnimator = animate().z(0.0f).scaleX(1.0f);
}
viewPropertyAnimator.scaleY(f).duration = 150;
}
You can add this sample code to any layout to achieve desired effect.
You need to do it the old way you do with normal ViewGroups, overriding the draw order of children. This example forces the first view to always be drawn last - you need to customize for your use case.
public TopForcingRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setChildrenDrawingOrderEnabled(true);
}
#Override protected int getChildDrawingOrder(int childCount, int i) {
if (i >= childCount - 1) return 0;
//when asked for the last view to draw, answer ZERO
else return i + 1;
//when asked for the i-th view to draw, answer "the next one"
}
Pay attention to off by one errors.

Paralax effect in app background

Im new in Android world. I want to put some parallax background effects in my app.
How can I do it? How to approach to this in Android?
Is there any productive way to create 2-3 layer parallax background? Is there some tool, or class in android API?
Or maybe I have to modify background image location or margins "manually" in code?
Im using API level 19.
I have tried to understand Paralloid library, but this is too big to understand without any explanation. Im new to Android and Java, im not familiar with all Layouts and other UI objects, however I'm familiar with MVC.
I started bounty, maybe someone can explain step by step how that library works.
This is what you can do:
In your activity/fragment layout file specify 2 ScrollView's (say background_sv and content_sv).
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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" >
<com.example.parallax.MyScrollView
android:id="#+id/background_sv"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/parallax_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="..." />
</com.example.parallax.MyScrollView>
<com.example.parallax.MyScrollView
android:id="#+id/content_sv"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</com.example.parallax.MyScrollView>
</RelativeLayout>
Add a dummy view in the content scrollview of the height of the background and make it transparent. Now, attach a scroll listener to the content_sv. When the content scrollview is scrolled, call
mBgScrollView.scrollTo(0, (int)(y /*scroll Of content_sv*/ / 2f));
The existing API's doesn't have the support to get the scroll events.
Hence, we need to create a Custom ScrollView, to provide the ScrollViewListener.
package com.example.parallax;
// imports;
public class MyScrollView extends ScrollView {
public interface ScrollViewListener {
void onScrollChanged(MyScrollView scrollView, int x, int y, int oldx, int oldy);
}
private ScrollViewListener scrollViewListener = null;
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScrollViewListener(ScrollViewListener scrollViewListener) {
this.scrollViewListener = scrollViewListener;
}
#Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if(scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
}
}
}
Here is the activity which hosts both the content ScrollView and background ScrollView
package com.example.parallax;
// imports;
public class ParallaxActivity extends Activity implements ScrollViewListener {
private MyScrollView mBgScrollView;
private MyScrollView mContentScrollView;
#Override
public void onCreate(Bundle savedInstanceState) {
mBgScrollView = findViewById(R.id.background_sv);
mContentScrollView = findViewById(R.id.content_sv);
mContentScrollView.setOnScrollListener(this);
}
// this is method for onScrollListener put values according to your need
#Override
public void onScrollChanged(MyScrollView scrollView, int x, int y, int oldx, int oldy) {
super.onScrollChanged(scrollView, x, y, oldx, oldy);
// when the content scrollview will scroll by say 100px,
// the background scrollview will scroll by 50px. It will
// look like a parallax effect where the background is
// scrolling with a different speed then the content scrollview.
mBgScrollView.scrollTo(0, (int)(y / 2f));
}
}
I think the question is unclear, so this is not really an answer so much as an attempt to clarify with more detail than I could include in a comment.
My question is about what kind of parallax effect you want to achieve. Given these three examples (they are demo apps you can install from the Play Store), which if any has the type of parallax effect you want? Please answer in a comment.
Paralloid Demo
Parallax Scroll Demo
Google IO App
Given an answer, we all will find it easier to help out. If you edit your question to include this information, it will be improved.
The following contains an example application published by the author of Paralloid:
https://github.com/chrisjenx/Paralloid/tree/master/paralloidexample
From the GitHub page under the 'Getting Started' section:
Layout
ScrollView
This is an example, please refer to the paralloidexample App for full
code.
<FrameLayout ..>
<FrameLayout
android:id="#+id/top_content"
android:layout_width="match_parent"
android:layout_height="192dp"/>
<uk.co.chrisjenx.paralloid.views.ParallaxScrollView
android:id="#+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:id="#+id/scroll_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="192dp"/>
</uk.co.chrisjenx.paralloid.views.ParallaxScrollView>
</FrameLayout>
Fragment
Inside your onViewCreated() or onCreateView().
//...
FrameLayout topContent = (FrameLayout) rootView.findViewById(R.id.top_content);
ScrollView scrollView = (ScrollView) rootView.findViewById(R.id.scroll_view);
if (scrollView instanceof Parallaxor) {
((Parallaxor) scrollView).parallaxViewBy(topContent, 0.5f);
}
// TODO: add content to top/scroll content
Thats it!
Have a look at the Parallaxor interface for applicable Parallax
methods.
Hope this helps!
Also, here is a link to Google's 'getting started' page for android.
Also, here is a link to a 'java tutorial for complete beginners'.
As well as link to some documentation about layouts, which 'define the visual structure for a user interface'.
That being said, you would use the layout to define what the interface looks like and use the subsequent example code to define what happens when you interact with it.
P.S. You can see the application in action here
I use the ParallaxScroll library. Very easy to use, good samples and well documented.
Here is how it can be done using ScrollView and it's background image. I've committed the code in github.
You need to extend the ScrollView and Drawable classes.
By default the ScrollView background height will be same as viewport height. To achieve the parallax effect, the background height should be larger and should be based on the ScrollView child height and the background scrolling factor we want to impose.
Background scroll factor of 1 indicates, background height is same as ScrollView child height and hence background will scroll with same offset as the child scrolls.
0.5 indicates, background height is 0.5 times ScrollView child extended height and will scroll 50% slower compared to the child contents. This effectively brings the parallax scrolling effect.
Call following method from ScrollView constructor:
void init() {
// Calculate background drawable size before first draw of scrollview
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
// Remove the listener
getViewTreeObserver().removeOnPreDrawListener(this);
mDrawable = (ParallaxDrawable) getBackground();
if(mDrawable != null && mDrawable instanceof ParallaxDrawable) {
// Get the only child of scrollview
View child = getChildAt(0);
int width = child.getWidth();
// calculate height of background based on child height and scroll factor
int height = (int) (getHeight() + (child.getHeight() - getHeight()) * mScrollFactor);
mDrawable.setSize(width, height);
}
return true;
}
});
}
When ScrollView is scrolled, take into consideration the scroll offset while drawing the background. This basically achieves the parallax effect.
ParallaxScrollView:
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
if(mDrawable != null && mDrawable instanceof ParallaxDrawable) {
// set the scroll offset for the background drawable.
mDrawable.setScrollOffset(x*mScrollFactor, y*mScrollFactor);
}
}
ParallaxDrawable:
#Override
public void draw(Canvas canvas) {
// To move the background up, translate canvas by negative offset
canvas.translate(-mScrollXOffset, -mScrollYOffset);
mDrawable.draw(canvas);
canvas.translate(mScrollXOffset, mScrollYOffset);
}
protected void onBoundsChange(Rect bounds) {
// This sets the size of background drawable.
mDrawable.setBounds(new Rect(bounds.top, bounds.left, bounds.left + mWidth, bounds.top + mHeight));
}
Usage of ParallaxScrollView and ParallaxDrawable:
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.parallax_layout);
final ParallaxScrollView scrollView = (ParallaxScrollView) findViewById(R.id.sv);
ParallaxDrawable drawable = new ParallaxDrawable(getResources().getDrawable(R.drawable.bg));
scrollView.setBackground( drawable, 0.2f );
}
}
parallax_layout.xml:
<manish.com.parallax.ParallaxScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/sv"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#fff"
android:text="#string/text" />
<View
android:layout_width="match_parent"
android:layout_height="5dp" />
...
</LinearLayout>
</manish.com.parallax.ParallaxScrollView>
The Android API does not support much concrete tools for it as you probably noticed. In API 20 they added elevation which is an attribute for depth. This does not support parallax layouts itself but I would say it's a step by Google to make this kind of work easier. If you want a wild guess on if and when, I would say that parallax utilities could be added before API 25 is released, based on the latest update and the progress in battery efficiency.
For now all you need is to listen for some kind of movement and change the views positions based on a value representing elevation.
Your question made me upgrade my own project and this is how I did it using ViewDragHelper inside a Fragment.
public class MainFragment extends Fragment implements View.OnTouchListener {
private ImageView mDecor, mBamboo, mBackgroundBamboo;
private RelativeLayout mRootLayout;
private ViewDragHelper mDragHelper;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mRootLayout = (RelativeLayout) inflater.inflate(R.layout.fragment_main, container, false);
mRootLayout.setOnTouchListener(this);
mDecor = (ImageView) mRootLayout.findViewById(R.id.decor);
mBamboo = (ImageView) mRootLayout.findViewById(R.id.bamboo);
mBackgroundBamboo = (ImageView) mRootLayout.findViewById(R.id.backround_bamboo);
mDragHelper = ViewDragHelper.create(mRootLayout, 1.0f, new ViewDragHelper.Callback() {
private final float MAX_LEFT = -0;
private final float MAX_TOP = -20;
private final float MAX_RIGHT = 50;
private final float MAX_BOTTOM = 10;
private final float MULTIPLIER = 0.1f;
private final int DECOR_ELEVATION = 3;
private final int FRONT_BAMBOO_ELEVATION = 6;
private final int BACKGROUND_BAMBOO_ELEVATION = 1;
private float mLeft = 0;
private float mTop = 0;
#Override
public boolean tryCaptureView(View view, int i) {
return true;
}
#Override
public int clampViewPositionVertical(View child, int top, int dy) {
mTop += dy * MULTIPLIER;
mTop = mTop > MAX_BOTTOM ? MAX_BOTTOM : mTop < MAX_TOP ? MAX_TOP : mTop;
mDecor.setTranslationY(mTop * DECOR_ELEVATION);
mBamboo.setTranslationY(mTop * FRONT_BAMBOO_ELEVATION);
mBackgroundBamboo.setTranslationY(mTop * BACKGROUND_BAMBOO_ELEVATION);
return 0;
}
#Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
mLeft += dx * MULTIPLIER;
mLeft = mLeft < MAX_LEFT ? MAX_LEFT : mLeft > MAX_RIGHT ? MAX_RIGHT : mLeft;
mDecor.setTranslationX(mLeft * DECOR_ELEVATION);
mBamboo.setTranslationX(mLeft * FRONT_BAMBOO_ELEVATION);
mBackgroundBamboo.setTranslationX(mLeft * BACKGROUND_BAMBOO_ELEVATION);
return 0;
}
#Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy){
mRootLayout.requestLayout();
}
});
return mRootLayout;
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
mDragHelper.processTouchEvent(motionEvent);
// you can still use this touch listener for buttons etc.
return true;
}
}
Hi You can go with the below-given code for ParallaxView class
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.util.ArrayList;
public class ParallaxView extends SurfaceView implements Runnable {
private volatile boolean running;
private Thread gameThread = null;
// For drawing
private Paint paint;
private Canvas canvas;
private SurfaceHolder ourHolder;
// Holds a reference to the Activity
Context context;
// Control the fps
long fps =60;
// Screen resolution
int screenWidth;
int screenHeight;
ParallaxView(Context context, int screenWidth, int screenHeight) {
super(context);
this.context = context;
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
// Initialize our drawing objects
ourHolder = getHolder();
paint = new Paint();
}
#Override
public void run() {
while (running) {
long startFrameTime = System.currentTimeMillis();
update();
draw();
// Calculate the fps this frame
long timeThisFrame = System.currentTimeMillis() - startFrameTime;
if (timeThisFrame >= 1) {
fps = 1000 / timeThisFrame;
}
}
}
private void update() {
// Update all the background positions
}
private void draw() {
if (ourHolder.getSurface().isValid()) {
//First we lock the area of memory we will be drawing to
canvas = ourHolder.lockCanvas();
//draw a background color
canvas.drawColor(Color.argb(255, 0, 3, 70));
// Draw the background parallax
// Draw the rest of the game
paint.setTextSize(60);
paint.setColor(Color.argb(255, 255, 255, 255));
canvas.drawText("I am a plane", 350, screenHeight / 100 * 5, paint);
paint.setTextSize(220);
canvas.drawText("I'm a train", 50, screenHeight / 100*80, paint);
// Draw the foreground parallax
// Unlock and draw the scene
ourHolder.unlockCanvasAndPost(canvas);
}
}
// Clean up our thread if the game is stopped
public void pause() {
running = false;
try {
gameThread.join();
} catch (InterruptedException e) {
// Error
}
}
// Make a new thread and start it
// Execution moves to our run method
public void resume() {
running = true;
gameThread = new Thread(this);
gameThread.start();
}
}// End of ParallaxView
To know more you can go **
here
**: http://gamecodeschool.com/android/coding-a-parallax-scrolling-background-for-android/

windowSoftInputMode="adjustResize" not working with translucent action/navbar

I have problems with the translucent actionbar/navbar in the new Android KitKat (4.4) and the windowSoftInputMode="adjustResize".
Normaly, changing the InputMode to adjustResize, the app should resize itself when keyboard is shown, but here it won't! If I delete the lines for the transparent effect, the resize is working.
So if the keyboard is visible, my ListView is under it and I can't access the last few items (only by hiding the keyboard manually).
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="XYZ"
android:versionCode="23"
android:versionName="0.1" >
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/Theme.XYZStyle" >
<activity
android:name="XYZ"
android:label="#string/app_name"
android:windowSoftInputMode="adjustResize" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
values-v19/styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.XYZStyle" parent="#style/Theme.AppCompat.Light">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
</resources>
fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/listView_contacts"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:divider="#null"
android:dividerHeight="0dp"
android:drawSelectorOnTop="true"
android:fastScrollAlwaysVisible="true"
android:fastScrollEnabled="true"
android:paddingBottom="#dimen/navigationbar__height" >
</ListView>
</RelativeLayout>
Any ideas for fixing this?
You are missing the following property:
android:fitsSystemWindows="true"
in the root RelativeLayout of the fragment .xml layout.
Update:
Last year there was an interesting talk by Chris Bane that explains in good detail how this works:
https://www.youtube.com/watch?v=_mGDMVRO3iE
There's a related bug report here. I've found a workaround that, from limited testing, seems to do the trick with no repercussions. Add a custom implementation of your root ViewGroup (I almost always am using FrameLayout, so this is what I've tested with) with the logic below. Then, use this custom layout in place of your root layout, and ensure you set android:fitsSystemWindows="true". You can then just call getInsets() any time after layout (e.g. add an OnPreDrawListener) to adjust the rest of your layout to account for the system insets, if desired.
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import org.jetbrains.annotations.NotNull;
/**
* #author Kevin
* Date Created: 3/7/14
*
* https://code.google.com/p/android/issues/detail?id=63777
*
* When using a translucent status bar on API 19+, the window will not
* resize to make room for input methods (i.e.
* {#link android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} and
* {#link android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_PAN} are
* ignored).
*
* To work around this; override {#link #fitSystemWindows(android.graphics.Rect)},
* capture and override the system insets, and then call through to FrameLayout's
* implementation.
*
* For reasons yet unknown, modifying the bottom inset causes this workaround to
* fail. Modifying the top, left, and right insets works as expected.
*/
public final class CustomInsetsFrameLayout extends FrameLayout {
private int[] mInsets = new int[4];
public CustomInsetsFrameLayout(Context context) {
super(context);
}
public CustomInsetsFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public final int[] getInsets() {
return mInsets;
}
#Override
protected final boolean fitSystemWindows(#NotNull Rect insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// Intentionally do not modify the bottom inset. For some reason,
// if the bottom inset is modified, window resizing stops working.
// TODO: Figure out why.
mInsets[0] = insets.left;
mInsets[1] = insets.top;
mInsets[2] = insets.right;
insets.left = 0;
insets.top = 0;
insets.right = 0;
}
return super.fitSystemWindows(insets);
}
}
Since fitSystemWindows was deprecated, please refer to the answer below to complete the workaround.
#kcoppock answer is really helpful, but fitSystemWindows was deprecated in API level 20
So since API 20 (KITKAT_WATCH) you should override onApplyWindowInsets
#Override
public final WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0,
insets.getSystemWindowInsetBottom()));
} else {
return insets;
}
}
If you want to customize the insets and you are targeting API level >=21 you can accomplish this without having to create a custom view group. By just setting fitsSystemWindows padding will be applied to your container view by default, which you may not want.
The version checks are built into this method and only devices >= 21 will execute the code inside the lambda. Kotlin example:
ViewCompat.setOnApplyWindowInsetsListener(container) { view, insets ->
insets.replaceSystemWindowInsets(0, 0, 0, insets.systemWindowInsetBottom).apply {
ViewCompat.onApplyWindowInsets(view, this)
}
}
Make sure your layout still sets the fitsSystemWindows flag otherwise the window insets listener will not be called.
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
/>
These sources are helpful:
https://medium.com/google-developers/why-would-i-want-to-fitssystemwindows-4e26d9ce1eec
https://medium.com/#azizbekian/windowinsets-24e241d4afb9
This worked for me to have translucent status bar and adjustResize in fragment:
Make a custom RelativeLayout as #Victor91 and #kcoppock said.
Use CustomRelativeLayout as parent layout for your fragment.
Declare theme with android:windowTranslucentStatus = true
The container Activity must be declared in Manifest with
android:windowSoftInputMode="adjustResize" and use the declared
theme
Please Use fitsSystemWindows on fragment root layout!
public class CustomRelativeLayout extends RelativeLayout {
private int[] mInsets = new int[4];
public CustomRelativeLayout(Context context) {
super(context);
}
public CustomRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public final WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
mInsets[0] = insets.getSystemWindowInsetLeft();
mInsets[1] = insets.getSystemWindowInsetTop();
mInsets[2] = insets.getSystemWindowInsetRight();
return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0,
insets.getSystemWindowInsetBottom()));
} else {
return insets;
}
}
}
Then in xml,
<com.blah.blah.CustomRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
</com.blah.blah.CustomRelativeLayout>
A small update on the helpful #Victor Rendina's answer caused by the replaceSystemWindowInsets and systemWindowInsetBottom methods deprecation.
Prerequisites:
API >= 21
implementation 'androidx.core:core-ktx:1.5.0-alpha02' at least
Kotlin extension:
fun View?.fitSystemWindowsAndAdjustResize() = this?.let { view ->
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
view.fitsSystemWindows = true
val bottom = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
WindowInsetsCompat
.Builder()
.setInsets(
WindowInsetsCompat.Type.systemBars(),
Insets.of(0, 0, 0, bottom)
)
.build()
.apply {
ViewCompat.onApplyWindowInsets(v, this)
}
}
}
Usage:
rootView.fitSystemWindowsAndAdjustResize()
where rootView is literally the root view of the layout :)
Note: if the extension does not work for your root view (I ran into this when having ConstraintLayout as the rootView) wrap the entire layout with a FrameLayout so that the FrameLayout becomes the new root view.
I had the same problem,
My Activity had a ScrollView as root view and with translucent statusbar activated it didn't resize correctly when keyboard showed... and conseguently the screen didn't scrolled hiding the input views.
Solution:
Moved everything (layout and activity logic) inside a new Fragment.
Then changed the Activity to only include this Fragment. Now everything works as expected!
This is the layout of the activity:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/contentView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true" />
Add this first at your root layout.
android:fitsSystemWindows="true"
When you use this approach, it becomes your responsibility to ensure that critical parts of your app's UI (for example, the built-in controls in a Maps application) don't end up getting covered by system bars. This could make your app unusable. In most cases you can handle this by adding the android:fitsSystemWindows attribute to your XML layout file, set to true. This adjusts the padding of the parent ViewGroup to leave space for the system windows. This is sufficient for most applications.
In some cases, however, you may need to modify the default padding to get the desired layout for your app. To directly manipulate how your content lays out relative to the system bars (which occupy a space known as the window's "content insets"), override fitSystemWindows(Rect insets). The fitSystemWindows() method is called by the view hierarchy when the content insets for a window have changed, to allow the window to adjust its content accordingly. By overriding this method you can handle the insets (and hence your app's layout) however you want.
https://developer.android.com/training/system-ui/status#behind
If you want to become a master window fitter, please watch the video from the android developer.
https://www.youtube.com/watch?v=_mGDMVRO3iE
Based on Joseph Johnson's workaround in Android How to adjust layout in Full Screen Mode when softkeyboard is visible
call this in onCreate() after setContentView() in your activity.
AndroidBug5497Workaround.assistActivity(this);
a litte different from original replace return (r.bottom - r.top); with return r.bottom; in computeUsableHeight()
for some reason, i must set my activity fitsSystemWindows attribute to false.
this workaround saved me. it's works well for me. hope can help you.
the implementation class is:
public class AndroidBug5497Workaround {
// For more information, see https://code.google.com/p/android/issues/detail?id=5497
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
public static void assistActivity (Activity activity) {
new AndroidBug5497Workaround(activity);
}
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private AndroidBug5497Workaround(Activity activity) {
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
});
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard/4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
} else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return r.bottom;
}
}
It shouldn't work with the translucent status bar; that setting forces the window into fullscreen mode which does not work with adjustResize.
You can either use adjustPan or use the fitsSystemWindows properties. I would suggest reading about the feature though, it has significant side effects:
https://medium.com/google-developers/why-would-i-want-to-fitssystemwindows-4e26d9ce1eec
I had like a problem.
I set windowDrawsSystemBarBackgrounds to 'true' and my app should show under status bar.
It's my activity theme.
<item name="android:windowTranslucentStatus" tools:targetApi="KITKAT">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:statusBarColor">#android:color/transparent</item>
and I got help from jianshu's blog.
you can read code but text like me.
I add few code more.
public final class ZeroInsetsFrameLayout extends FrameLayout {
private int[] mInsets = new int[4];
public ZeroInsetsFrameLayout(Context context) {
super(context);
}
public ZeroInsetsFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ZeroInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public final int[] getInsets() {
return mInsets;
}
#Override
public WindowInsets computeSystemWindowInsets(WindowInsets in, Rect outLocalInsets) {
outLocalInsets.left = 0;
outLocalInsets.top = 0;
outLocalInsets.right = 0;
return super.computeSystemWindowInsets(in, outLocalInsets);
}
#Override
protected final boolean fitSystemWindows(#NonNull Rect insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// Intentionally do not modify the bottom inset. For some reason,
// if the bottom inset is modified, window resizing stops working.
// TODO: Figure out why.
mInsets[0] = insets.left;
mInsets[1] = insets.top;
mInsets[2] = insets.right;
insets.left = 0;
insets.top = 0;
insets.right = 0;
}
return super.fitSystemWindows(insets);
}
}
This is my fragment layout.
<com.dhna.widget.ZeroInsetsFrameLayout 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:fitsSystemWindows="true"
android:background="#color/white">
<!-- your xml code -->
</ZeroInsetsFrameLayout>
I want it to be helpful to you.
good luck!
AndroidBug5497Workaround.java take care memory leak. need below code
getViewTreeObserver().removeOnGlobalLayoutListener(listener);
My sample using RxJava that automatically call removeOnGlobalLayoutListener() when onPause() in Activity's lifecycle
public class MyActivity extends RxAppCompatActivity {
// ...
protected void onStart(){
super.onStart();
TRSoftKeyboardVisibility
.changes(this) // activity
.compose(this.<TRSoftKeyboardVisibility.ChangeEvent>bindUntilEvent(ActivityEvent.PAUSE))
.subscribe(keyboardEvent -> {
FrameLayout content = (FrameLayout) findViewById(android.R.id.content);
View firstChildView = content.getChildAt(0);
firstChildView.getLayoutParams().height = keyboardEvent.viewHeight();
firstChildView.requestLayout();
// keyboardEvent.isVisible = keyboard visible or not
// keyboardEvent.keyboardHeight = keyboard height
// keyboardEvent.viewHeight = fullWindowHeight - keyboardHeight
});
//...
}
package commonlib.rxjava.keyboard;
import android.app.Activity;
import android.view.View;
import android.widget.FrameLayout;
import kr.ohlab.android.util.Assert;
import rx.Observable;
public class TRSoftKeyboardVisibility {
public static Observable<ChangeEvent> changes(Activity activity) {
Assert.notNull(activity, "activity == null");
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
View childOfContent = content.getChildAt(0);
return Observable.create(
new TRSoftKeyboardVisibilityEventOnSubscribe(childOfContent));
}
public static final class ChangeEvent {
private final int keyboardHeight;
private final boolean visible;
private final int viewHeight;
public static ChangeEvent create(boolean visible, int keyboardHeight,
int windowDisplayHeight) {
return new ChangeEvent(visible, keyboardHeight, windowDisplayHeight);
}
private ChangeEvent(boolean visible, int keyboardHeight, int viewHeight) {
this.keyboardHeight = keyboardHeight;
this.visible = visible;
this.viewHeight = viewHeight;
}
public int keyboardHeight() {
return keyboardHeight;
}
public boolean isVisible() {
return this.visible;
}
public int viewHeight() {
return viewHeight;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ChangeEvent)) return false;
ChangeEvent that = (ChangeEvent) o;
if (keyboardHeight != that.keyboardHeight) return false;
if (visible != that.visible) return false;
return viewHeight == that.viewHeight;
}
#Override
public int hashCode() {
int result = keyboardHeight;
result = 31 * result + (visible ? 1 : 0);
result = 31 * result + viewHeight;
return result;
}
#Override
public String toString() {
return "ChangeEvent{" +
"keyboardHeight=" + keyboardHeight +
", visible=" + visible +
", viewHeight=" + viewHeight +
'}';
}
}
}
package commonlib.rxjava.keyboard;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import kr.ohlab.android.util.Assert;
import rx.Observable;
import rx.Subscriber;
import rx.android.MainThreadSubscription;
import timber.log.Timber;
public class TRSoftKeyboardVisibilityEventOnSubscribe
implements Observable.OnSubscribe<TRSoftKeyboardVisibility.ChangeEvent> {
private final View mTopView;
private int mLastVisibleDecorViewHeight;
private final Rect mWindowVisibleDisplayFrame = new Rect();
public TRSoftKeyboardVisibilityEventOnSubscribe(View topView) {
mTopView = topView;
}
private int computeWindowFrameHeight() {
mTopView.getWindowVisibleDisplayFrame(mWindowVisibleDisplayFrame);
return (mWindowVisibleDisplayFrame.bottom - mWindowVisibleDisplayFrame.top);
}
private TRSoftKeyboardVisibility.ChangeEvent checkKeyboardVisibility() {
int windowFrameHeightNow = computeWindowFrameHeight();
TRSoftKeyboardVisibility.ChangeEvent event = null;
if (windowFrameHeightNow != mLastVisibleDecorViewHeight) {
int mTopViewHeight = mTopView.getHeight();
int heightDiff = mTopViewHeight - windowFrameHeightNow;
Timber.e("XXX heightDiff=" + heightDiff);
if (heightDiff > (mTopViewHeight / 4)) {
event = TRSoftKeyboardVisibility.ChangeEvent.create(true, heightDiff, windowFrameHeightNow);
} else {
event = TRSoftKeyboardVisibility.ChangeEvent.create(false, 0, windowFrameHeightNow);
}
mLastVisibleDecorViewHeight = windowFrameHeightNow;
return event;
}
return null;
}
public void call(final Subscriber<? super TRSoftKeyboardVisibility.ChangeEvent> subscriber) {
Assert.checkUiThread();
final ViewTreeObserver.OnGlobalLayoutListener listener =
new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
TRSoftKeyboardVisibility.ChangeEvent event = checkKeyboardVisibility();
if( event == null)
return;
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(event);
}
}
};
mTopView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
subscriber.add(new MainThreadSubscription() {
#Override
protected void onUnsubscribe() {
mTopView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
});
}
}
After I had researched on all forum. thoese ways can not help find point out. Lucky when i tried doing this way. It helps me resolved problem
XML
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<!-- Your xml -->
</RelativeLayout>
Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView("Your Activity");
setAdjustScreen();
}
Created Func
protected void setAdjustScreen(){
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
/*android:windowSoftInputMode="adjustPan|adjustResize"*/
}
Finally adding some lines to your mainifest
<activity
android:name="Your Activity"
android:windowSoftInputMode="adjustPan|adjustResize"
android:screenOrientation="portrait"></activity>
I had the same problem. I have solved using coordinatorlayout
activity.main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:layout_height="match_parent" android:layout_width="match_parent"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:layout_height="?attr/actionBarSize"
android:layout_width="match_parent"
app:popupTheme="#style/AppTheme.PopupOverlay"
android:background="?attr/colorPrimary"
android:id="#+id/toolbar"/>
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_main2"/>
</android.support.design.widget.CoordinatorLayout>
content_main2.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.v7.widget.RecyclerView
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_marginTop="30dp"
android:layout_marginBottom="30dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:id="#+id/post_msg_recyclerview">
</android.support.v7.widget.RecyclerView>
<EditText
android:layout_width="match_parent"
android:layout_height="50dp"
app:layout_constraintBottom_toBottomOf="parent"
android:background="#color/colorPrimary"
/>
</android.support.constraint.ConstraintLayout>
MainActivity.java
now add this line linearLayoutManager.setStackFromEnd(true);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(linearLayoutManager);
Adapter adapter1=new Adapter(arrayList);
recyclerView.setAdapter(adapter1);
<androidx.constraintlayout.widget.ConstraintLayout
android:fitsSystemWindows="true">
<androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.appbar.CollapsingToolbarLayout/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView>
<Editext/>
<androidx.core.widget.NestedScrollView/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
here is what i use
in the main view in the xml file you will add this
android:animateLayoutChanges="true"
then in the "onCreate" function you will before every thing get the status bar size like this
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0)
{
status_bar=getResources().getDimensionPixelSize(resourceId);
}
then finally in "onCreate" you will add this to update the size
main_view= findViewById(R.id.the_main);
main_view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
Rect r = new Rect();
View view = getWindow().getDecorView();
view.getWindowVisibleDisplayFrame(r);
if (Math.abs(old_size - r.height()) > 100)
{
ViewGroup.LayoutParams params = main_view.getLayoutParams();
params.height = r.height()+ status_bar ;
main_view.setLayoutParams(params);
}
old_size = r.height();
}
});
I don't why but option adjustResize doesn't work with fullscreen. I've just added titleBar and works ( android:theme="#style/AppTheme"). Insted that I use in code " getSupportActionBar().hide();"
<activity
android:name=".ChatActivity"
android:launchMode="singleInstance"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"
android:theme="#style/AppTheme"
/>
The best practice allows user scroll content when the keyboard is shown.
So to add this functionality you need put your root layout inside the ScrollView and use windowSoftInputMode="adjustResize" activity method.
But if you want to use this functionality with <item name="android:windowTranslucentStatus">true</item>
flag on Android 5 content won't be scrollable and will overlaps with keyboard.
To solve this issue check this answer

Replacement for the linearLayout weights mechanism

Background:
Google suggests to avoid using nested weighted linearLayouts because of performance.
using nested weighted linearLayout is awful to read, write and maintain.
there is still no good alternative for putting views that are % of the available size. Only solutions are weights and using OpenGL. There isn't even something like the "viewBox" shown on WPF/Silverlight to auto-scale things.
This is why I've decided to create my own layout which you tell for each of its children exactly what should be their weights (and surrounding weights) compared to its size.
It seems I've succeeded , but for some reason I think there are some bugs which I can't track down.
One of the bugs is that textView, even though I give a lot of space for it, it puts the text on the top instead of in the center. imageViews on the other hand work very well. Another bug is that if I use a layout (for example a frameLayout) inside my customized layout, views within it won't be shown (but the layout itself will).
Please help me figure out why it occurs.
How to use: instead of the next usage of linear layout (I use a long XML on purpose, to show how my solution can shorten things):
<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">
<View android:layout_width="wrap_content" android:layout_height="0px"
android:layout_weight="1" />
<LinearLayout android:layout_width="match_parent"
android:layout_height="0px" android:layout_weight="1"
android:orientation="horizontal">
<View android:layout_width="0px" android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView android:layout_width="0px" android:layout_weight="1"
android:layout_height="match_parent" android:text="#string/hello_world"
android:background="#ffff0000" android:gravity="center"
android:textSize="20dp" android:textColor="#ff000000" />
<View android:layout_width="0px" android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
<View android:layout_width="wrap_content" android:layout_height="0px"
android:layout_weight="1" />
</LinearLayout>
What I do is simply (the x is where to put the view itself in the weights list):
<com.example.weightedlayouttest.WeightedLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.example.weightedlayouttest"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" tools:context=".MainActivity">
<TextView android:layout_width="0px" android:layout_height="0px"
app:horizontalWeights="1,1x,1" app:verticalWeights="1,1x,1"
android:text="#string/hello_world" android:background="#ffff0000"
android:gravity="center" android:textSize="20dp" android:textColor="#ff000000" />
</com.example.weightedlayouttest.WeightedLayout>
My code of the special layout is:
public class WeightedLayout extends ViewGroup
{
#Override
protected WeightedLayout.LayoutParams generateDefaultLayoutParams()
{
return new WeightedLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
}
#Override
public WeightedLayout.LayoutParams generateLayoutParams(final AttributeSet attrs)
{
return new WeightedLayout.LayoutParams(getContext(),attrs);
}
#Override
protected ViewGroup.LayoutParams generateLayoutParams(final android.view.ViewGroup.LayoutParams p)
{
return new WeightedLayout.LayoutParams(p.width,p.height);
}
#Override
protected boolean checkLayoutParams(final android.view.ViewGroup.LayoutParams p)
{
final boolean isCorrectInstance=p instanceof WeightedLayout.LayoutParams;
return isCorrectInstance;
}
public WeightedLayout(final Context context)
{
super(context);
}
public WeightedLayout(final Context context,final AttributeSet attrs)
{
super(context,attrs);
}
public WeightedLayout(final Context context,final AttributeSet attrs,final int defStyle)
{
super(context,attrs,defStyle);
}
#Override
protected void onLayout(final boolean changed,final int l,final int t,final int r,final int b)
{
for(int i=0;i<this.getChildCount();++i)
{
final View v=getChildAt(i);
final WeightedLayout.LayoutParams layoutParams=(WeightedLayout.LayoutParams)v.getLayoutParams();
//
final int availableWidth=r-l;
final int totalHorizontalWeights=layoutParams.getLeftHorizontalWeight()+layoutParams.getViewHorizontalWeight()+layoutParams.getRightHorizontalWeight();
final int left=l+layoutParams.getLeftHorizontalWeight()*availableWidth/totalHorizontalWeights;
final int right=r-layoutParams.getRightHorizontalWeight()*availableWidth/totalHorizontalWeights;
//
final int availableHeight=b-t;
final int totalVerticalWeights=layoutParams.getTopVerticalWeight()+layoutParams.getViewVerticalWeight()+layoutParams.getBottomVerticalWeight();
final int top=t+layoutParams.getTopVerticalWeight()*availableHeight/totalVerticalWeights;
final int bottom=b-layoutParams.getBottomVerticalWeight()*availableHeight/totalVerticalWeights;
//
v.layout(left+getPaddingLeft(),top+getPaddingTop(),right+getPaddingRight(),bottom+getPaddingBottom());
}
}
// ///////////////
// LayoutParams //
// ///////////////
public static class LayoutParams extends ViewGroup.LayoutParams
{
int _leftHorizontalWeight =0,_rightHorizontalWeight=0,_viewHorizontalWeight=0;
int _topVerticalWeight =0,_bottomVerticalWeight=0,_viewVerticalWeight=0;
public LayoutParams(final Context context,final AttributeSet attrs)
{
super(context,attrs);
final TypedArray arr=context.obtainStyledAttributes(attrs,R.styleable.WeightedLayout_LayoutParams);
{
final String horizontalWeights=arr.getString(R.styleable.WeightedLayout_LayoutParams_horizontalWeights);
//
// handle horizontal weight:
//
final String[] words=horizontalWeights.split(",");
boolean foundViewHorizontalWeight=false;
int weight;
for(final String word : words)
{
final int viewWeightIndex=word.lastIndexOf('x');
if(viewWeightIndex>=0)
{
if(foundViewHorizontalWeight)
throw new IllegalArgumentException("found more than one weights for the current view");
weight=Integer.parseInt(word.substring(0,viewWeightIndex));
setViewHorizontalWeight(weight);
foundViewHorizontalWeight=true;
}
else
{
weight=Integer.parseInt(word);
if(weight<0)
throw new IllegalArgumentException("found negative weight:"+weight);
if(foundViewHorizontalWeight)
_rightHorizontalWeight+=weight;
else _leftHorizontalWeight+=weight;
}
}
if(!foundViewHorizontalWeight)
throw new IllegalArgumentException("couldn't find any weight for the current view. mark it with 'x' next to the weight value");
}
//
// handle vertical weight:
//
{
final String verticalWeights=arr.getString(R.styleable.WeightedLayout_LayoutParams_verticalWeights);
final String[] words=verticalWeights.split(",");
boolean foundViewVerticalWeight=false;
int weight;
for(final String word : words)
{
final int viewWeightIndex=word.lastIndexOf('x');
if(viewWeightIndex>=0)
{
if(foundViewVerticalWeight)
throw new IllegalArgumentException("found more than one weights for the current view");
weight=Integer.parseInt(word.substring(0,viewWeightIndex));
setViewVerticalWeight(weight);
foundViewVerticalWeight=true;
}
else
{
weight=Integer.parseInt(word);
if(weight<0)
throw new IllegalArgumentException("found negative weight:"+weight);
if(foundViewVerticalWeight)
_bottomVerticalWeight+=weight;
else _topVerticalWeight+=weight;
}
}
if(!foundViewVerticalWeight)
throw new IllegalArgumentException("couldn't find any weight for the current view. mark it with 'x' next to the weight value");
}
//
arr.recycle();
}
public LayoutParams(final int width,final int height)
{
super(width,height);
}
public LayoutParams(final ViewGroup.LayoutParams source)
{
super(source);
}
public int getLeftHorizontalWeight()
{
return _leftHorizontalWeight;
}
public void setLeftHorizontalWeight(final int leftHorizontalWeight)
{
_leftHorizontalWeight=leftHorizontalWeight;
}
public int getRightHorizontalWeight()
{
return _rightHorizontalWeight;
}
public void setRightHorizontalWeight(final int rightHorizontalWeight)
{
if(rightHorizontalWeight<0)
throw new IllegalArgumentException("negative weight :"+rightHorizontalWeight);
_rightHorizontalWeight=rightHorizontalWeight;
}
public int getViewHorizontalWeight()
{
return _viewHorizontalWeight;
}
public void setViewHorizontalWeight(final int viewHorizontalWeight)
{
if(viewHorizontalWeight<0)
throw new IllegalArgumentException("negative weight:"+viewHorizontalWeight);
_viewHorizontalWeight=viewHorizontalWeight;
}
public int getTopVerticalWeight()
{
return _topVerticalWeight;
}
public void setTopVerticalWeight(final int topVerticalWeight)
{
if(topVerticalWeight<0)
throw new IllegalArgumentException("negative weight :"+topVerticalWeight);
_topVerticalWeight=topVerticalWeight;
}
public int getBottomVerticalWeight()
{
return _bottomVerticalWeight;
}
public void setBottomVerticalWeight(final int bottomVerticalWeight)
{
if(bottomVerticalWeight<0)
throw new IllegalArgumentException("negative weight :"+bottomVerticalWeight);
_bottomVerticalWeight=bottomVerticalWeight;
}
public int getViewVerticalWeight()
{
return _viewVerticalWeight;
}
public void setViewVerticalWeight(final int viewVerticalWeight)
{
if(viewVerticalWeight<0)
throw new IllegalArgumentException("negative weight :"+viewVerticalWeight);
_viewVerticalWeight=viewVerticalWeight;
}
}
}
I accepted your challenge and attempted to create the layout you describe in response to my comment. You are right. It is surprisingly difficult to accomplish. Besides that, I do like shooting house flies. So I jumped on board and came up with this solution.
Extend the existing layout classes rather than creating your own from scratch. I went with RelativeLayout to start with but the same approach can be used by all of them. This gives you the ability to use the default behavior for that layout on child views that you don't want to manipulate.
I added four attributes to the layout called top, left, width and height. My intention was to mimic HTML by allowing values such as "10%", "100px", "100dp" etc.. At this time the only value accepted is an integer representing the % of parent. "20" = 20% of the layout.
For better performance I allow the super.onLayout() to execute through all of it's iterations and only manipulate the views with the custom attributes on it's last pass. Since these views will be positioned and scaled independently of the siblings we can move them after everything else has settled.
Here is atts.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="HtmlStyleLayout">
<attr name="top" format="integer"/>
<attr name="left" format="integer"/>
<attr name="height" format="integer"/>
<attr name="width" format="integer"/>
</declare-styleable>
</resources>
Here is my layout class.
package com.example.helpso;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
public class HtmlStyleLayout extends RelativeLayout{
private int pass =0;
#Override
protected HtmlStyleLayout.LayoutParams generateDefaultLayoutParams()
{
return new HtmlStyleLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,RelativeLayout.LayoutParams.WRAP_CONTENT);
}
#Override
public HtmlStyleLayout.LayoutParams generateLayoutParams(final AttributeSet attrs)
{
return new HtmlStyleLayout.LayoutParams(getContext(),attrs);
}
#Override
protected RelativeLayout.LayoutParams generateLayoutParams(final android.view.ViewGroup.LayoutParams p)
{
return new HtmlStyleLayout.LayoutParams(p.width,p.height);
}
#Override
protected boolean checkLayoutParams(final android.view.ViewGroup.LayoutParams p)
{
final boolean isCorrectInstance=p instanceof HtmlStyleLayout.LayoutParams;
return isCorrectInstance;
}
public HtmlStyleLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScaleType(View v){
try{
((ImageView) v).setScaleType (ImageView.ScaleType.FIT_XY);
}catch (Exception e){
// The view is not an ImageView
}
}
#Override
protected void onLayout(final boolean changed,final int l,final int t,final int r,final int b)
{
super.onLayout(changed, l, t, r, b); //Let the parent layout do it's thing
pass++; // After the last pass of
final int childCount = this.getChildCount(); // the parent layout
if(true){ // we do our thing
for(int i=0;i<childCount;++i)
{
final View v=getChildAt(i);
final HtmlStyleLayout.LayoutParams params = (HtmlStyleLayout.LayoutParams)v.getLayoutParams();
int newTop = v.getTop(); // set the default value
int newLeft = v.getLeft(); // of these to the value
int newBottom = v.getBottom(); // set by super.onLayout()
int newRight= v.getRight();
boolean viewChanged = false;
if(params.getTop() >= 0){
newTop = ( (int) ((b-t) * (params.getTop() * .01)) );
viewChanged = true;
}
if(params.getLeft() >= 0){
newLeft = ( (int) ((r-l) * (params.getLeft() * .01)) );
viewChanged = true;
}
if(params.getHeight() > 0){
newBottom = ( (int) ((int) newTop + ((b-t) * (params.getHeight() * .01))) );
setScaleType(v); // set the scale type to fitxy
viewChanged = true;
}else{
newBottom = (newTop + (v.getBottom() - v.getTop()));
Log.i("heightElse","v.getBottom()=" +
Integer.toString(v.getBottom())
+ " v.getTop=" +
Integer.toString(v.getTop()));
}
if(params.getWidth() > 0){
newRight = ( (int) ((int) newLeft + ((r-l) * (params.getWidth() * .01))) );
setScaleType(v);
viewChanged = true;
}else{
newRight = (newLeft + (v.getRight() - v.getLeft()));
}
// only call layout() if we changed something
if(viewChanged)
Log.i("SizeLocation",
Integer.toString(i) + ": "
+ Integer.toString(newLeft) + ", "
+ Integer.toString(newTop) + ", "
+ Integer.toString(newRight) + ", "
+ Integer.toString(newBottom));
v.layout(newLeft, newTop, newRight, newBottom);
}
pass = 0; // reset the parent pass counter
}
}
public class LayoutParams extends RelativeLayout.LayoutParams
{
private int top, left, width, height;
public LayoutParams(final Context context, final AttributeSet atts) {
super(context, atts);
TypedArray a = context.obtainStyledAttributes(atts, R.styleable.HtmlStyleLayout);
top = a.getInt(R.styleable.HtmlStyleLayout_top , -1);
left = a.getInt(R.styleable.HtmlStyleLayout_left, -1);
width = a.getInt(R.styleable.HtmlStyleLayout_width, -1);
height = a.getInt(R.styleable.HtmlStyleLayout_height, -1);
a.recycle();
}
public LayoutParams(int w, int h) {
super(w,h);
Log.d("lp","2");
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
Log.d("lp","3");
}
public LayoutParams(ViewGroup.MarginLayoutParams source) {
super(source);
Log.d("lp","4");
}
public int getTop(){
return top;
}
public int getLeft(){
return left;
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
}
}
Here is an example activity xml
<com.example.helpso.HtmlStyleLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:html="http://schemas.android.com/apk/res/com.example.helpso"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:scaleType="fitXY"
android:src="#drawable/bg" />
<ImageView
android:id="#+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/overlay"
html:height="10"
html:left="13"
html:top="18"
html:width="23" />
</com.example.helpso.HtmlStyleLayout>
Here are the images I used for testing.
If you do not set a value for a particular attribute it's default will be used. So if you set width but not height the image will scale in width and wrap_content for height.
Zipped project folder.
apk
I found the source of the bug. The problem is that I was using the layout's child count as in indicator of how many calls to onLayout it will make. This doesn't seem to hold true in older versions of android. I noticed in 2.1 onLayout is only called once. So I changed
if(pass == childCount){
to
if(true){
and it started working as expected.
I still thinks it's beneficial to adjust the layout only after the super is done. Just need to find a better way to know when that is.
EDIT
I didn't realize that your intention was to patch together images with pixel by pixel precision. I achieved the precision you are looking for by using double float precision variables instead of integers. However, you will not be able accomplish this while allowing your images to scale. When an images is scaled up pixels are added at some interval between the existing pixels. The color of the new pixels are some weighted average of the surrounding pixels. When you scale the images independently of each other they don't share any information. The result is that you will always have some artifact at the seam. Add to that the result of rounding since you can't have a partial pixel and you will always have a +/-1 pixel tolerance.
To verify this you can attempt the same task in your premium photo editing software. I use PhotoShop. Using the same images as in my apk, I placed them in seperate files. I scaled them both by 168% vertically and 127% horizontally. I then placed them in a file together and attempted to align them. The result was exactly the same as is seen in my apk.
To demonstrate the accuracy of the layout, I added a second activity to my apk. On this activity I did not scale the background image. Everything else is exactly the same. The result is seamless.
I also added a button to show/hide the overlay image and one to switch between the activities.
I updated both the apk and the zipped project folder on my google drive. You can get them by the links above.
After trying your code, I just find the reason of the problems you mentioned, and it is because in your customed layout, you only layout the child properly, however you forgot to measure your child properly, which will directly affect the drawing hierarchy, so simply add the below code, and it works for me.
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec)-this.getPaddingRight()-this.getPaddingRight();
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec)-this.getPaddingTop()-this.getPaddingBottom();
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if(heightMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.UNSPECIFIED)
throw new IllegalArgumentException("the layout must have a exact size");
for (int i = 0; i < this.getChildCount(); ++i) {
View child = this.getChildAt(i);
LayoutParams lp = (LayoutParams)child.getLayoutParams();
int width = lp._viewHorizontalWeight * widthSize/(lp._leftHorizontalWeight+lp._rightHorizontalWeight+lp._viewHorizontalWeight);
int height = lp._viewVerticalWeight * heightSize/(lp._topVerticalWeight+lp._bottomVerticalWeight+lp._viewVerticalWeight);
child.measure(width | MeasureSpec.EXACTLY, height | MeasureSpec.EXACTLY);
}
this.setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
}
Now there is a nicer solution than the custom layout I've made:
PercentRelativeLayout
Tutorial can be found here and a repo can be found here.
Example code:
<android.support.percent.PercentRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"
app:layout_marginTopPercent="25%"
app:layout_marginLeftPercent="25%"/>
</android.support.percent.PercentFrameLayout/>
or:
<android.support.percent.PercentFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"
app:layout_marginTopPercent="25%"
app:layout_marginLeftPercent="25%"/>
</android.support.percent.PercentFrameLayout/>
I wonder though if it can handle the issues I've shown here.
I propose to use following optimizations:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:gravity="center">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="#string/hello_world"
android:background="#ffff0000" android:gravity="center"
android:textSize="20dp" android:textColor="#ff000000" />
</FrameLayout>
or use http://developer.android.com/reference/android/widget/LinearLayout.html#attr_android:weightSum
or use TableLayout with layout_weight for rows and columns
or use GridLayout.

Android How to adjust layout in Full Screen Mode when softkeyboard is visible

I have researched a lot to adjust the layout when softkeyboard is active and I have successfully implemented it but the problem comes when I use android:theme="#android:style/Theme.NoTitleBar.Fullscreen" this in my activity tag in manifest file.
For this I have used android:windowSoftInputMode="adjustPan|adjustResize|stateHidden" with different options but no luck.
After that I implemented FullScreen programmatically and tried various layout to work with FullScreen but all in vain.
I referred these links and have looked many posts here related to this issue:
http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html
http://davidwparker.com/2011/08/30/android-how-to-float-a-row-above-keyboard/
Here is xml code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="#+id/masterContainerView"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#ffffff">
<ScrollView android:id="#+id/parentScrollView"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<LinearLayout android:layout_width="fill_parent"
android:layout_height="fill_parent" android:orientation="vertical">
<TextView android:id="#+id/setup_txt" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Setup - Step 1 of 3"
android:textColor="#color/top_header_txt_color" android:textSize="20dp"
android:padding="8dp" android:gravity="center_horizontal" />
<TextView android:id="#+id/txt_header" android:layout_width="fill_parent"
android:layout_height="40dp" android:text="AutoReply:"
android:textColor="#color/top_header_txt_color" android:textSize="14dp"
android:textStyle="bold" android:padding="10dp"
android:layout_below="#+id/setup_txt" />
<EditText android:id="#+id/edit_message"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:text="Some text here." android:textSize="16dp"
android:textColor="#color/setting_editmsg_color" android:padding="10dp"
android:minLines="5" android:maxLines="6" android:layout_below="#+id/txt_header"
android:gravity="top" android:scrollbars="vertical"
android:maxLength="132" />
<ImageView android:id="#+id/image_bottom"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_below="#+id/edit_message" />
</LinearLayout>
</ScrollView>
<RelativeLayout android:id="#+id/scoringContainerView"
android:layout_width="fill_parent" android:layout_height="50px"
android:orientation="vertical" android:layout_alignParentBottom="true"
android:background="#535254">
<Button android:id="#+id/btn_save" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_alignParentRight="true"
android:layout_marginTop="7dp" android:layout_marginRight="15dp"
android:layout_below="#+id/edit_message"
android:text = "Save" />
<Button android:id="#+id/btn_cancel" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_marginTop="7dp"
android:layout_marginRight="10dp" android:layout_below="#+id/edit_message"
android:layout_toLeftOf="#+id/btn_save" android:text = "Cancel" />
</RelativeLayout>
</RelativeLayout>
I want the bottom 2 buttons should go upward when the softkeyboard comes in picture.
Based on yghm's workaround, I coded up a convenience class that allows me to solve the problem with a one-liner (after adding the new class to my source code of course). The one-liner is:
AndroidBug5497Workaround.assistActivity(this);
And the implementation class is:
public class AndroidBug5497Workaround {
// For more information, see https://issuetracker.google.com/issues/36911528
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
public static void assistActivity (Activity activity) {
new AndroidBug5497Workaround(activity);
}
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private AndroidBug5497Workaround(Activity activity) {
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
});
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard/4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
} else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);
}
}
Since the answer has already been picked and problem known to be a bug, I thought I would add a "Possible Work Around".
You can toggle fullScreen mode when soft keyboard is shown. This allows the "adjustPan" to work correctly.
In other words, I still use #android:style/Theme.Black.NoTitleBar.Fullscreen as part of the application theme and stateVisible|adjustResize as part of the activity window soft input mode but to get them to work together I must toggle fullscreen mode before the keyboard comes up.
Use the following Code:
Turn Off full screen mode
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
Turn On full screen mode
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
Note - inspiration came from: Hiding Title in a Fullscreen mode
I tried the solution from Joseph Johnson, but like others I ran into the gap-between-content-and-keyboard problem. The problem occurs because the soft input mode is always pan when using full-screen mode. This panning interferes with Joseph's solution when you activate an input field that would be hidden by the soft input.
When the soft input appears, the content is first panned based on its original height, and then resized by the layout requested by the Joseph's solution. The resizing and subsequent layout do not undo the panning, which results in the gap. The full order of events is:
Global layout listener
Panning
Layout of content (= actual resizing of content)
It is not possible to disable panning, but it is possible to force the pan offset to be 0 by changing the height of the content. This can be done in the listener, because it is run before panning takes place. Setting the content height to the available height results in a smooth user experience, i.e. no flickering.
I also made these changes. If any of these introduce issues, let me know:
Switched determination of available height to use getWindowVisibleDisplayFrame. The Rect is cached to prevent a little bit of unneeded garbage.
Allow the listener to be removed too. This is useful when you reuse an activity for different fragments having different full-screen requirements.
Do not distinguish between keyboard shown or hidden, but always set the content height to the visible display frame height.
It has been tested on a Nexus 5, and emulators running API levels 16-24 with screen sizes ranging from tiny to big.
The code has been ported to Kotlin, but porting my changes back to Java is simple. Let me know if you need help:
class AndroidBug5497Workaround constructor(activity: Activity) {
private val contentContainer = activity.findViewById(android.R.id.content) as ViewGroup
private val rootView = contentContainer.getChildAt(0)
private val rootViewLayout = rootView.layoutParams as FrameLayout.LayoutParams
private val viewTreeObserver = rootView.viewTreeObserver
private val listener = ViewTreeObserver.OnGlobalLayoutListener { possiblyResizeChildOfContent() }
private val contentAreaOfWindowBounds = Rect()
private var usableHeightPrevious = 0
// I call this in "onResume()" of my fragment
fun addListener() {
viewTreeObserver.addOnGlobalLayoutListener(listener)
}
// I call this in "onPause()" of my fragment
fun removeListener() {
viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
private fun possiblyResizeChildOfContent() {
contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)
val usableHeightNow = contentAreaOfWindowBounds.height()
if (usableHeightNow != usableHeightPrevious) {
rootViewLayout.height = usableHeightNow
// Change the bounds of the root view to prevent gap between keyboard and content, and top of content positioned above top screen edge.
rootView.layout(contentAreaOfWindowBounds.left, contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom)
rootView.requestLayout()
usableHeightPrevious = usableHeightNow
}
}
}
I just found a simple and reliable solution if you are using the system UI approach (https://developer.android.com/training/system-ui/immersive.html).
It works in the case when you are using View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN, e.g. if you are using CoordinatorLayout.
It won't work for WindowManager.LayoutParams.FLAG_FULLSCREEN (The one you can also set in theme with android:windowFullscreen), but you can achieve similar effect with SYSTEM_UI_FLAG_LAYOUT_STABLE (which "has the same visual effect" according to the docs) and this solution should work again.
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION /* If you want to hide navigation */
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
I've tested it on my device running Marshmallow.
The key is that soft keyboards are also one of the system windows (such as status bar and navigation bar), so the WindowInsets dispatched by system contains accurate and reliable information about it.
For the use case such as in DrawerLayout where we are trying to draw behind the status bar, We can create a layout that ignores only the top inset, and applies the bottom inset which accounts for the soft keyboard.
Here is my custom FrameLayout:
/**
* Implements an effect similar to {#code android:fitsSystemWindows="true"} on Lollipop or higher,
* except ignoring the top system window inset. {#code android:fitsSystemWindows="true"} does not
* and should not be set on this layout.
*/
public class FitsSystemWindowsExceptTopFrameLayout extends FrameLayout {
public FitsSystemWindowsExceptTopFrameLayout(Context context) {
super(context);
}
public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setPadding(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(),
insets.getSystemWindowInsetBottom());
return insets.replaceSystemWindowInsets(0, insets.getSystemWindowInsetTop(), 0, 0);
} else {
return super.onApplyWindowInsets(insets);
}
}
}
And to use it:
<com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Your original layout here -->
</com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout>
This should theoretically work for any device without insane modification, much better than any hack that tries to take a random 1/3 or 1/4 of screen size as reference.
(It requires API 16+, but I'm using fullscreen only on Lollipop+ for drawing behind the status bar so it's the best solution in this case.)
Please note that android:windowSoftInputMode="adjustResize" does not work when WindowManager.LayoutParams.FLAG_FULLSCREENis set for an activity. You've got two options.
Either disable fullscreen mode for your activity. Activity is not re-sized in fullscreen mode. You can do this either in xml (by changing the theme of the activity) or in Java code. Add the following lines in your onCreate() method.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);`
OR
Use an alternative way to achieve fullscreen mode. Add the following code in your onCreate() method.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
View decorView = getWindow().getDecorView();
// Hide the status bar.
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);`
Please note that method-2 only works in Android 4.1 and above.
I had to face this problem too and had a work around which i checked on HTC one, galaxy s1, s2, s3, note and HTC sensation.
put a global layout listener on the root view of your layout
mRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){
public void onGlobalLayout() {
checkHeightDifference();
}
});
and in there i checked the height difference and if the height difference of the screen is bigger then a third on the screen height then we can assume the keyboard is open.
took it from this answer.
private void checkHeightDifference(){
// get screen frame rectangle
Rect r = new Rect();
mRootView.getWindowVisibleDisplayFrame(r);
// get screen height
int screenHeight = mRootView.getRootView().getHeight();
// calculate the height difference
int heightDifference = screenHeight - (r.bottom - r.top);
// if height difference is different then the last height difference and
// is bigger then a third of the screen we can assume the keyboard is open
if (heightDifference > screenHeight/3 && heightDifference != mLastHeightDifferece) {
// keyboard visiblevisible
// get root view layout params
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mRootView.getLayoutParams();
// set the root view height to screen height minus the height difference
lp.height = screenHeight - heightDifference;
// call request layout so the changes will take affect
.requestLayout();
// save the height difference so we will run this code only when a change occurs.
mLastHeightDifferece = heightDifference;
} else if (heightDifference != mLastHeightDifferece) {
// keyboard hidden
PFLog.d("[ChatroomActivity] checkHeightDifference keyboard hidden");
// get root view layout params and reset all the changes we have made when the keyboard opened.
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mRootView.getLayoutParams();
lp.height = screenHeight;
// call request layout so the changes will take affect
mRootView.requestLayout();
// save the height difference so we will run this code only when a change occurs.
mLastHeightDifferece = heightDifference;
}
}
this is probably not bullet proof and maybe on some devices it will not work but it worked for me and hope it will help you too.
Add android:fitsSystemWindows="true" to the layout, and this layout will resize.
I implemented Joseph Johnson solution and it worked well, I noticed after using this solution sometimes the drawer on the application will not close properly.
I added a functionality to remove the listener removeOnGlobalLayoutListener when the user closes the fragment where are edittexts located.
//when the application uses full screen theme and the keyboard is shown the content not scrollable!
//with this util it will be scrollable once again
//http://stackoverflow.com/questions/7417123/android-how-to-adjust-layout-in-full-screen-mode-when-softkeyboard-is-visible
public class AndroidBug5497Workaround {
private static AndroidBug5497Workaround mInstance = null;
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private ViewTreeObserver.OnGlobalLayoutListener _globalListener;
// For more information, see https://code.google.com/p/android/issues/detail?id=5497
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
public static AndroidBug5497Workaround getInstance (Activity activity) {
if(mInstance==null)
{
synchronized (AndroidBug5497Workaround.class)
{
mInstance = new AndroidBug5497Workaround(activity);
}
}
return mInstance;
}
private AndroidBug5497Workaround(Activity activity) {
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
_globalListener = new ViewTreeObserver.OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
possiblyResizeChildOfContent();
}
};
}
public void setListener()
{
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(_globalListener);
}
public void removeListener()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mChildOfContent.getViewTreeObserver().removeOnGlobalLayoutListener(_globalListener);
} else {
mChildOfContent.getViewTreeObserver().removeGlobalOnLayoutListener(_globalListener);
}
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard/4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
} else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);
}
}
uses the class where is my edittexts located
#Override
public void onStart()
{
super.onStart();
AndroidBug5497Workaround.getInstance(getActivity()).setListener();
}
#Override
public void onStop()
{
super.onStop();
AndroidBug5497Workaround.getInstance(getActivity()).removeListener();
}
I'm currently using this approach and it works like a charm. The trick is we get keyboard height from different methods on 21 above and below and then use it as the bottom padding of our root view in our activity. I assumed your layout does not need a top padding (goes below status bar) but in case you do, inform me to update my answer.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout mainLayout = findViewById(R.id.main_layout);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ViewCompat.setOnApplyWindowInsetsListener(mainLayout , new OnApplyWindowInsetsListener() {
#Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
v.setPadding(0, 0, 0, insets.getSystemWindowInsetBottom());
return insets;
}
});
} else {
View decorView = getWindow().getDecorView();
final View contentView = mainLayout;
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
decorView.getWindowVisibleDisplayFrame(r);
//get screen height and calculate the difference with the useable area from the r
int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels;
int diff = height - r.bottom;
//if it could be a keyboard add the padding to the view
if (diff != 0) {
// if the use-able screen height differs from the total screen height we assume that it shows a keyboard now
//check if the padding is 0 (if yes set the padding for the keyboard)
if (contentView.getPaddingBottom() != diff) {
//set the padding of the contentView for the keyboard
contentView.setPadding(0, 0, 0, diff);
}
} else {
//check if the padding is != 0 (if yes reset the padding)
if (contentView.getPaddingBottom() != 0) {
//reset the padding of the contentView
contentView.setPadding(0, 0, 0, 0);
}
}
}
});
}
}
...
}
Don't forget to address your root view with an id:
activity_main.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
Hope it helps someone.
To get it to work with FullScreen:
Use the ionic keyboard plugin. This allows you to listen for when the keyboard appears and disappears.
OnDeviceReady add these event listeners:
// Allow Screen to Move Up when Keyboard is Present
window.addEventListener('native.keyboardshow', onKeyboardShow);
// Reset Screen after Keyboard hides
window.addEventListener('native.keyboardhide', onKeyboardHide);
The Logic:
function onKeyboardShow(e) {
// Get Focused Element
var thisElement = $(':focus');
// Get input size
var i = thisElement.height();
// Get Window Height
var h = $(window).height()
// Get Keyboard Height
var kH = e.keyboardHeight
// Get Focused Element Top Offset
var eH = thisElement.offset().top;
// Top of Input should still be visible (30 = Fixed Header)
var vS = h - kH;
i = i > vS ? (vS - 30) : i;
// Get Difference
var diff = (vS - eH - i);
if (diff < 0) {
var parent = $('.myOuter-xs.myOuter-md');
// Add Padding
var marginTop = parseInt(parent.css('marginTop')) + diff - 25;
parent.css('marginTop', marginTop + 'px');
}
}
function onKeyboardHide(e) {
// Remove All Style Attributes from Parent Div
$('.myOuter-xs.myOuter-md').removeAttr('style');
}
Basically if they difference is minus then that is the amount of pixels that the keyboard is covering of your input. So if you adjust your parent div by this that should counteract it.
Adding timeouts to the logic say 300ms should also optimise performance (as this will allow keyboard time to appear.
I tried Joseph Johnson's class, and it worked, but didn't quite meet my needs. Rather than emulating android:windowSoftInputMode="adjustResize", I needed to emulate android:windowSoftInputMode="adjustPan".
I am using this for a full screen webview. To pan the content view to the correct position, I need to use a javascript interface which provides details on the position of the page element which has focus and thus is receiving the keyboard input. I have omitted those details, but provided my rewrite of Joseph Johnson's class. It will provide a very solid base for you to implement a custom pan vs. his resize.
package some.package.name;
import some.package.name.JavaScriptObject;
import android.app.Activity;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
//-------------------------------------------------------
// ActivityPanner Class
//
// Convenience class to handle Activity attributes bug.
// Use this class instead of windowSoftInputMode="adjustPan".
//
// To implement, call enable() and pass a reference
// to an Activity which already has its content view set.
// Example:
// setContentView( R.layout.someview );
// ActivityPanner.enable( this );
//-------------------------------------------------------
//
// Notes:
//
// The standard method for handling screen panning
// when the virtual keyboard appears is to set an activity
// attribute in the manifest.
// Example:
// <activity
// ...
// android:windowSoftInputMode="adjustPan"
// ... >
// Unfortunately, this is ignored when using the fullscreen attribute:
// android:theme="#android:style/Theme.NoTitleBar.Fullscreen"
//
//-------------------------------------------------------
public class ActivityPanner {
private View contentView_;
private int priorVisibleHeight_;
public static void enable( Activity activity ) {
new ActivityPanner( activity );
}
private ActivityPanner( Activity activity ) {
FrameLayout content = (FrameLayout)
activity.findViewById( android.R.id.content );
contentView_ = content.getChildAt( 0 );
contentView_.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() { panAsNeeded(); }
});
}
private void panAsNeeded() {
// Get current visible height
int currentVisibleHeight = visibleHeight();
// Determine if visible height changed
if( currentVisibleHeight != priorVisibleHeight_ ) {
// Determine if keyboard visiblity changed
int screenHeight =
contentView_.getRootView().getHeight();
int coveredHeight =
screenHeight - currentVisibleHeight;
if( coveredHeight > (screenHeight/4) ) {
// Keyboard probably just became visible
// Get the current focus elements top & bottom
// using a ratio to convert the values
// to the native scale.
float ratio = (float) screenHeight / viewPortHeight();
int elTop = focusElementTop( ratio );
int elBottom = focusElementBottom( ratio );
// Determine the amount of the focus element covered
// by the keyboard
int elPixelsCovered = elBottom - currentVisibleHeight;
// If any amount is covered
if( elPixelsCovered > 0 ) {
// Pan by the amount of coverage
int panUpPixels = elPixelsCovered;
// Prevent panning so much the top of the element
// becomes hidden
panUpPixels = ( panUpPixels > elTop ?
elTop : panUpPixels );
// Prevent panning more than the keyboard height
// (which produces an empty gap in the screen)
panUpPixels = ( panUpPixels > coveredHeight ?
coveredHeight : panUpPixels );
// Pan up
contentView_.setY( -panUpPixels );
}
}
else {
// Keyboard probably just became hidden
// Reset pan
contentView_.setY( 0 );
}
// Save usabale height for the next comparison
priorVisibleHeight_ = currentVisibleHeight;
}
}
private int visibleHeight() {
Rect r = new Rect();
contentView_.getWindowVisibleDisplayFrame( r );
return r.bottom - r.top;
}
// Customize this as needed...
private int viewPortHeight() { return JavaScriptObject.viewPortHeight(); }
private int focusElementTop( final float ratio ) {
return (int) (ratio * JavaScriptObject.focusElementTop());
}
private int focusElementBottom( final float ratio ) {
return (int) (ratio * JavaScriptObject.focusElementBottom());
}
}
1) Create KeyboardHeightHelper:
public class KeyboardHeightHelper {
private final View decorView;
private int lastKeyboardHeight = -1;
public KeyboardHeightHelper(Activity activity, View activityRootView, OnKeyboardHeightChangeListener listener) {
this.decorView = activity.getWindow().getDecorView();
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
int keyboardHeight = getKeyboardHeight();
if (lastKeyboardHeight != keyboardHeight) {
lastKeyboardHeight = keyboardHeight;
listener.onKeyboardHeightChange(keyboardHeight);
}
});
}
private int getKeyboardHeight() {
Rect rect = new Rect();
decorView.getWindowVisibleDisplayFrame(rect);
return decorView.getHeight() - rect.bottom;
}
public interface OnKeyboardHeightChangeListener {
void onKeyboardHeightChange(int keyboardHeight);
}
}
2) Let your activity be full screen:
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
3) Listen for keyboard height changes and add bottom padding for your view:
View rootView = activity.findViewById(R.id.root); // your root view or any other you want to resize
KeyboardHeightHelper effectiveHeightHelper = new KeyboardHeightHelper(
activity,
rootView,
keyboardHeight -> rootView.setPadding(0, 0, 0, keyboardHeight));
So, each time keyboard will appear on the screen - bottom padding for your view will change, and content will be rearranged.
Indeed the soft keyboard appearance doesn't seem to affect the Activity in any way no matter what windowSoftInputMode I select in the FullScreen mode.
Though I couldn't find much documentation on this property, I think that the FullScreen mode was designed for gaming application which do not require much use of the soft keyboard. If yours is an Activity which requires user interaction through soft keyboard, please reconsider using a non-FullScreen theme. You could turn off the TitleBar using a NoTitleBar theme. Why would you want to hide the notification bar?
Just keep as android:windowSoftInputMode="adjustResize". Because it is given to keep only one out of "adjustResize" and "adjustPan"(The window adjustment mode is specified with either adjustResize or adjustPan. It is highly recommended that you always specify one or the other). You can find it out here:
http://developer.android.com/resources/articles/on-screen-inputs.html
It works perfectly for me.
only use android:windowSoftInputMode="adjustResize|stateHidden as you use AdjustPan then it disable the resizing property
I used Joseph Johnson created AndroidBug5497Workaround class but getting black space between softkeyboard and the view. I referred this link Greg Ennis. After doing some changes to the above this is my final working code.
public class SignUpActivity extends Activity {
private RelativeLayout rlRootView; // this is my root layout
private View rootView;
private ViewGroup contentContainer;
private ViewTreeObserver viewTreeObserver;
private ViewTreeObserver.OnGlobalLayoutListener listener;
private Rect contentAreaOfWindowBounds = new Rect();
private FrameLayout.LayoutParams rootViewLayout;
private int usableHeightPrevious = 0;
private View mDecorView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_up);
mDecorView = getWindow().getDecorView();
contentContainer =
(ViewGroup) this.findViewById(android.R.id.content);
listener = new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
};
rootView = contentContainer.getChildAt(0);
rootViewLayout = (FrameLayout.LayoutParams)
rootView.getLayoutParams();
rlRootView = (RelativeLayout) findViewById(R.id.rlRootView);
rlRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int heightDiff = rlRootView.getRootView().getHeight() - rlRootView.getHeight();
if (heightDiff > Util.dpToPx(SignUpActivity.this, 200)) {
// if more than 200 dp, it's probably a keyboard...
// Logger.info("Soft Key Board ", "Key board is open");
} else {
Logger.info("Soft Key Board ", "Key board is CLOSED");
hideSystemUI();
}
}
});
}
// This snippet hides the system bars.
protected void hideSystemUI() {
// Set the IMMERSIVE flag.
// Set the content to appear under the system bars so that the
content
// doesn't resize when the system bars hide and show.
mDecorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
#Override
protected void onPause() {
super.onPause();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.removeOnGlobalLayoutListener(listener);
}
}
#Override
protected void onResume() {
super.onResume();
if (viewTreeObserver == null || !viewTreeObserver.isAlive()) {
viewTreeObserver = rootView.getViewTreeObserver();
}
viewTreeObserver.addOnGlobalLayoutListener(listener);
}
#Override
protected void onDestroy() {
super.onDestroy();
rootView = null;
contentContainer = null;
viewTreeObserver = null;
}
private void possiblyResizeChildOfContent() {
contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);
int usableHeightNow = contentAreaOfWindowBounds.height();
if (usableHeightNow != usableHeightPrevious) {
rootViewLayout.height = usableHeightNow;
rootView.layout(contentAreaOfWindowBounds.left,
contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
rootView.requestLayout();
usableHeightPrevious = usableHeightNow;
} else {
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
}
}
}
based on https://stackoverflow.com/a/19494006/1815624 and desire to make it happen...
updated idea
combining answers from
https://stackoverflow.com/a/19494006/1815624
https://stackoverflow.com/a/10952394/1815624
Relevant code:
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
} else {
// keyboard probably just became hidden
if(usableHeightPrevious != 0) {
frameLayoutParams.height = usableHeightSansKeyboard;
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
Full Source at https://github.com/CrandellWS/AndroidBug5497Workaround/blob/master/AndroidBug5497Workaround.java
old idea
Create a static value of the containers height before opening the keyboard
Set the container height based on usableHeightSansKeyboard - heightDifference when the keyboard opens and set it back to the saved value when it closes
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
int mStatusHeight = getStatusBarHeight();
frameLayoutParams.topMargin = mStatusHeight;
((MainActivity)activity).setMyMainHeight(usableHeightSansKeyboard - heightDifference);
if(BuildConfig.DEBUG){
Log.v("aBug5497", "keyboard probably just became visible");
}
} else {
// keyboard probably just became hidden
if(usableHeightPrevious != 0) {
frameLayoutParams.height = usableHeightSansKeyboard;
((MainActivity)activity).setMyMainHeight();
}
frameLayoutParams.topMargin = 0;
if(BuildConfig.DEBUG){
Log.v("aBug5497", "keyboard probably just became hidden");
}
}
Methods in MainActivity
public void setMyMainHeight(final int myMainHeight) {
runOnUiThread(new Runnable() {
#Override
public void run() {
ConstraintLayout.LayoutParams rLparams = (ConstraintLayout.LayoutParams) myContainer.getLayoutParams();
rLparams.height = myMainHeight;
myContainer.setLayoutParams(rLparams);
}
});
}
int mainHeight = 0;
public void setMyMainHeight() {
runOnUiThread(new Runnable() {
#Override
public void run() {
ConstraintLayout.LayoutParams rLparams = (ConstraintLayout.LayoutParams) myContainer.getLayoutParams();
rLparams.height = mainHeight;
myContainer.setLayoutParams(rLparams);
}
});
}
Example Container XML
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.constraint.ConstraintLayout
android:id="#+id/my_container"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintHeight_percent=".8">
similarly margins can be added if needed...
Another consideration is use padding an example of this can be found at:
https://github.com/mikepenz/MaterialDrawer/issues/95#issuecomment-80519589
private void resizeWindowOnKeyboardVisible() {
RelativeLayout rootLayout;
rootLayout = findViewById(R.id.rootLayout);
this.getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
ViewGroup.LayoutParams layoutParams = rootLayout.getLayoutParams();
int height ;
#Override
public void onGlobalLayout() {
Rect r = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
int screenHeight = rootLayout.getContext().getResources().getDisplayMetrics().heightPixels;
int heightDiff = screenHeight - r.bottom;
if (heightDiff > screenHeight*0.15)
{
height = screenHeight - heightDiff;
layoutParams.height=height;
rootLayout.setLayoutParams(layoutParams);
}else{
height=ViewGroup.LayoutParams.MATCH_PARENT;
if( height!=layoutParams.height) {
layoutParams.height = height;
rootLayout.setLayoutParams(layoutParams);
}
}
}
});
}
Using android:windowSoftInputMode="adjustResize|stateHidden might not work in all cases and also android:fitsSystemWindows="true doesn't help when you use SYSTEM_UI_FLAG_FULLSCREEN tags. To make view/window/webview adjustable when Keyboard visible do the following things.
Use RelativeLayout as root layout.
Declare the above method resizeWindowOnKeyboardVisible() in an activity & call it after setContentView() in onCreate() method.
It works in Android 11 (API 30) also.
Based on #Sdghasemi's solution, here's my Kotlin code, without the deprecated insets.getSystemWindowInsetBottom(). Also I added a padding animation to make the keyboard opening smoother.
val rootLayout = findViewById<RelativeLayout>(R.id.your_root_layout)
ViewCompat.setOnApplyWindowInsetsListener(rootLayout) { v, insets ->
val animator = ValueAnimator.ofInt(0, insets.getInsets(WindowInsetsCompat.Type.ime()).bottom))
animator.addUpdateListener {
valueAnimator -> v.setPadding(0, 0, 0, valueAnimator.animatedValue as? Int ?: 0)
}
animator.duration = 200
animator.start()
insets
}
Call it from the onCreate() method of your Activity.
In my case, this snippet works better than setting android:windowSoftInputMode="adjustPan" in the AndroidManifest.xml
You want the bottom bar to stick to the bottom of the view, but when the keyboard is displayed, they should move up to be placed above the keyboard, right?
You can try this code snippet:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
...>
<RelativeLayout
android:id="#+id/RelativeLayoutTopBar"
...>
</RelativeLayout>
<LinearLayout
android:id="#+id/LinearLayoutBottomBar"
android:layout_alignParentBottom = true
...>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="390dp"
android:orientation="vertical"
android:layout_above="#+id/LinearLayoutBottomBar"
android:layout_below="#+id/RelativeLayoutTopBar">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:id="#+id/ScrollViewBackground">
...
</ScrollView>
</LinearLayout>
</RelativeLayout>
The BottomBar will stick to the bottom of the view and the LinearLayout containing the ScrollView will take what's left of the view after the top/bottom bar and the keyboard are displayed. Let me know if it works for you as well.
Thank you Joseph for your answer. However, in the method possiblyResizeChildOfContent(), the portion
else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
was not working for me, as the lower portion of view became hidden.
So I had to take a global variable restoreHeight, and in the constructor, I inserted the last line
restoreHeight = frameLayoutParams.height;
and then I replaced the former mentioned part with
else {
// keyboard probably just became hidden
frameLayoutParams.height = restoreHeight;
}
But I have no idea why your code didn't work for me. It would be of great help, if someone can shed light on this.
I was only using full screen mode to hide the status bar. However, I want the app to resize when keyboard is shown. All of the other solutions (likely due to age of post) were complicated or not possible for my use (want to avoid change Java code for sack of PhoneGap Build).
Instead of using Full screen, I modified my configure for Android to be non-fullscreen:
<preference name="fullscreen" value="false" />
And added the cordova-plugin-statusbar, via command line:
cordova plugin add cordova-plugin-statusbar
When app has loaded, I simple call a method on the plugin to hide itself, like:
if (window.cordova && window.cordova.platformId == 'android' && window.StatusBar)
window.StatusBar.hide();
This works like a charm. Only real downside is that the status bar is breifly visible while the app loads. For my needs, that wasn't an issue.
I have tried out all the possible answers from stackOverflow, finally i solved after a week Long search .
I have used the coordinate layout and i changed this with linearLayout and my problem is fixed. I dont know possibly the coordinate layout has bugs or anything my mistake.
I tried many solutions include Joseph Johnson's and Johan Stuyts's. But as a result I got a white space between content and keyboard on some devices (like Lenovo s820) in all cases.
So I made some changes to their codes and finally got working solution.
My idea based on adding margin to top of content when keyboard is showing.
contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);
int usableHeightNow = contentAreaOfWindowBounds.height();
if (usableHeightNow != usableHeightPrevious) {
int difference = usableHeightNow - usableHeightPrevious;
if (difference < 0 && difference < -150) {
keyboardShowed = true;
rootViewLayout.topMargin -= difference + 30;
rootViewLayout.bottomMargin += 30;
}
else if (difference < 0 && difference > -150){
rootViewLayout.topMargin -= difference + 30;
}
else if (difference > 0 && difference > 150) {
keyboardShowed = false;
rootViewLayout.topMargin = 0;
rootViewLayout.bottomMargin = 0;
}
rootView.requestLayout();
Log.e("Bug Workaround", "Difference: " + difference);
usableHeightPrevious = usableHeightNow;
}
As you can see, I add 30 px to difference because there is a small white space between top of the screen and content zone with margin. And I dont know whence it appears so I decided just make margins smaller and now it works exactly how I needed.
Today not working adjustResize on full screen issue is actual for android sdk.
From answers i found:
the solution - but solution has this showing on picture issue :
Than i found the solution and remove the one unnecessary action:
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
So, see my fixed solution code on Kotlin:
class AndroidBug5497Workaround constructor(val activity: Activity) {
private val content = activity.findViewById<View>(android.R.id.content) as FrameLayout
private val mChildOfContent = content.getChildAt(0)
private var usableHeightPrevious: Int = 0
private val contentContainer = activity.findViewById(android.R.id.content) as ViewGroup
private val rootView = contentContainer.getChildAt(0)
private val rootViewLayout = rootView.layoutParams as FrameLayout.LayoutParams
private val listener = {
possiblyResizeChildOfContent()
}
fun addListener() {
mChildOfContent.apply {
viewTreeObserver.addOnGlobalLayoutListener(listener)
}
}
fun removeListener() {
mChildOfContent.apply {
viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
}
private fun possiblyResizeChildOfContent() {
val contentAreaOfWindowBounds = Rect()
mChildOfContent.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)
val usableHeightNow = contentAreaOfWindowBounds.height()
if (usableHeightNow != usableHeightPrevious) {
rootViewLayout.height = usableHeightNow
rootView.layout(contentAreaOfWindowBounds.left,
contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
mChildOfContent.requestLayout()
usableHeightPrevious = usableHeightNow
}
}
}
My bug fixing implement code:
class LeaveDetailActivity : BaseActivity(){
private val keyBoardBugWorkaround by lazy {
AndroidBug5497Workaround(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onResume() {
keyBoardBugWorkaround.addListener()
super.onResume()
}
override fun onPause() {
keyBoardBugWorkaround.removeListener()
super.onPause()
}
}
There is another way, without creating own helper classes or functions that calculate the height of the screen. Instead use ViewCompat.setOnApplyWindowInsetsListener.
With the listener you can check if the keyboard is open and set the bottom padding based on the keyboard height.
// the root view of your webview, e.g FrameLayout or LinearLayout
rootView = view.findViewById(R.id.whatever);
ViewCompat.setOnApplyWindowInsetsListener(rootView, (webView, insets) -> {
// checks if keyboard is visible, the Type.ime() stands for Input Method
boolean isKeyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
// get the keyboard height and use the height as bottom padding for your view
int bottomKeyboardPadding = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
if (isKeyboardVisible) { webView.setPadding(0, 0, 0, bottomKeyboardPadding); }
else { webView.setPadding(0, 0, 0, 0); }
return insets;
});
If you want to really support full screen with soft input:
private fun View.setStatusBarTransparent() {
this#MainActivity.apply {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.statusBarColor = ContextCompat.getColor(this, R.color.transparent)
this#setStatusBarTransparent.fitsSystemWindows = true
WindowCompat.setDecorFitsSystemWindows(window, false)
ViewCompat.setOnApplyWindowInsetsListener(this#setStatusBarTransparent) { root, windowInset ->
val inset = windowInset.getInsets(WindowInsetsCompat.Type.systemBars())
val inset2 = windowInset.getInsets(WindowInsetsCompat.Type.ime())
root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = inset.left
bottomMargin = maxOf(inset.bottom, inset2.bottom)
rightMargin = inset.right
}
WindowInsetsCompat.CONSUMED
}
}
}
Don't use:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
because works bad.
Instead of that, use:
fun setFullScreen(fullScreen: Boolean) {
val decorView = getWindow().getDecorView()
val uiOptions : Int
if(fullScreen){
uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN // this hide statusBar
toolbar.visibility = View.GONE // if you use toolbar
tabs.visibility = View.GONE // if you use tabLayout
} else {
uiOptions = View.SYSTEM_UI_FLAG_VISIBLE // this show statusBar
toolbar.visibility = View.VISIBLE
tabs.visibility = View.VISIBLE
}
decorView.setSystemUiVisibility(uiOptions)
}
In my case, this issue started happening once I added Crosswalk to my Cordova application. My app is not used in fullscreen and android:windowSoftInputMode="adjustPan".
I already had the ionic keyboard plugin in the application, so detecting if the keyboard was up or down was easy thanks to it:
// Listen for events to when the keyboard is opened and closed
window.addEventListener("native.keyboardshow", keyboardUp, false);
window.addEventListener('native.keyboardhide', keyboardDown, false);
function keyboardUp()
{
$('html').addClass('keyboardUp');
}
function keyboardDown()
{
$('html').removeClass('keyboardUp');
}
I tried all of the fixes above but the simple line that ended up doing it for me was this bit of css:
&.keyboardUp {
overflow-y: scroll;
}
Hope this saves you the few days I spent on this. :)

Categories

Resources