Friday, December 23, 2016

How to intercept event Soft keyboard shown/hidden

This is solution that is composed of code from SO and my enhancement to support various API levels.
Inspired by SO post http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android

Updated 1/24/2017

Updated 15 Apr. 2017
I hope, this solution is final.

Preparation class:

package my.package.utils;

import android.graphics.Rect;
import android.os.Build;
import android.view.View;
import android.view.ViewTreeObserver;

import java.util.LinkedList;
import java.util.List;

public class SoftKeyboardStateWatcher
        implements ViewTreeObserver.OnGlobalLayoutListener {

    public void onStart() {
        isSoftKeyboardOpened = false;
    }

    public interface SoftKeyboardStateListener {
        void onSoftKeyboardOpened(int keyboardHeightInPx);

        void onSoftKeyboardClosed();

    }

    private SoftKeyboardStateListener listener;

    private final View activityRootView;
    private boolean isSoftKeyboardOpened;
    private int resize;
    private int mKeyboardHeight;

    public SoftKeyboardStateWatcher(
            View activityRootView,
            SoftKeyboardStateListener listener
    ) {
        this(activityRootView, false);
        this.listener = listener;
    }

    private SoftKeyboardStateWatcher(
            View activityRootView,
            boolean isSoftKeyboardOpened
    ) {
        this.activityRootView = activityRootView;
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @Override    public void onGlobalLayout() {
        Rect r = new Rect();
        activityRootView.getWindowVisibleDisplayFrame(r);
        int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
        int correction = r.top;
        if (!isSoftKeyboardOpened && heightDiff > correction &&
                heightDiff > getStatusBarHeight() + getNavBarHeight()) {
            isSoftKeyboardOpened = true;
            notifyOnSoftKeyboardOpened(heightDiff, correction);
        } else {
            if (isSoftKeyboardOpened && heightDiff <= correction) {
                isSoftKeyboardOpened = false;
                notifyOnSoftKeyboardClosed();
            }else{
                if (!isSoftKeyboardOpened) return;
                if (mKeyboardHeight == heightDiff - correction) return;
                //Keyboard height has changed
            }
        }
    }

    private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx, int correction) {
        if (listener == null) return;
        mKeyboardHeight = keyboardHeightInPx - correction;
        listener.onSoftKeyboardOpened(keyboardHeightInPx);
    }

    private void notifyOnSoftKeyboardClosed() {
        if (listener == null) return;
        listener.onSoftKeyboardClosed();
    }

    public void removeSoftKeyboardStateListener() {
        listener = null;
    }

    private int getStatusBarHeight() {
        int result = 0;
        Resources resources = activityRootView.getResources();
        int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = resources.getDimensionPixelSize(resourceId);
        }
        return result;
    }

    private int getNavBarHeight() {
        Resources resources = activityRootView.getResources();
        int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId > 0) {
            return resources.getDimensionPixelSize(resourceId);
        }
        return 0;
    }

    public boolean isSoftKeyboardOpened() {
        return isSoftKeyboardOpened;
    }

}



The next step:
Your activity must implement 
SoftKeyboardStateWatcher.SoftKeyboardStateListener

Declare in your activity

protected SoftKeyboardStateWatcher mSoftKeyboardStateWatcher;

And run following code in onCreate:

int rootId = getRootId();//here you must supply root View id
View root;
if (rootId == 0 || (root = findViewById(rootId)) == null) return;
mSoftKeyboardStateWatcher = new SoftKeyboardStateWatcher(root);
mSoftKeyboardStateWatcher.addSoftKeyboardStateListener(his);

Now you have these methods:

@Override
public void onSoftKeyboardOpened(int keyboardHeightInPx) {
}

@Override
public void onSoftKeyboardClosed() {
}

Also do not forget to remove listener:

@Override
public void onDestroy() {
    if (mSoftKeyboardStateWatcher != null)
        mSoftKeyboardStateWatcher.removeSoftKeyboardStateListener(this);
    super.onDestroy();
}

Thursday, February 11, 2016

RecyclerView: Some trick with Fragment which was instantiated inside XML

Case:
Fragment inside xml.
Use of view is to use with RecyclerView viewholder.
Sometimes strange RuntimeException occurs: android.content.res.Resources$NotFoundException: Unable to find resource ID ...
XML looks like this:

...

 <someviewgroup android:layout_height="wrap_content" android:layout_width="wrap_content">

  <fragment android:id="+@id/someId" android:layout_height="100dp" android:layout_width="100dp" android:name="bla.bla.YourFragment">

 </fragment></someviewgroup>

...

If you inflate this XML you can face with android.content.res.Resources$NotFoundException.

And stacktrace guides to nowhere:

E/AndroidRuntime(1013): FATAL EXCEPTION: main
E/AndroidRuntime(1013): android.content.res.Resources$NotFoundException: Unable to find resource ID #0xffffffff
E/AndroidRuntime(1013):     at android.content.res.Resources.getResourceName(Resources.java:1659)
E/AndroidRuntime(1013):     at android.support.v4.app.FragmentManagerImpl ......  and etc.

The only way (I could find) to fix crash is to remove id.

But how to get access to instance of Fragment bla.bla.YourFragment from ViewHolder?
You cannot find fragment by id or tag.

Solution is:

In your fragment add callback as tag to rootview:

 public void onViewCreated(View view, Bundle savedInstanceState) {
  view .setTag(new SomeHandler() {
    @Override
    public void someMethod() {
     //Some code
    }
    });
  }

In ViewHolder find that View and get tag (there is no need to explain how to do it).
Voila, you can pass almost any data into your Fragment!


Wednesday, February 10, 2016

Case: You have an EditText and some Button to send text you typing. And you expect following behavior of soft keyboard: after Button been pressed keyboard must be in same place, i.e. do not disappear. Trick is: set null as OnTouchListener to Button.