कीबोर्ड विजिबिलिटी क्यों जांचें?
- UI एडजस्ट करना (बटन ऊपर ले जाएं, बॉटम बार छुपाएं)
- इनपुट वैलिडेशन ट्रिगर करना
- एनालिटिक्स: उपयोगकर्ता इंटरैक्शन ट्रैक करना
- एक्सेसिबिलिटी: स्क्रीन रीडर व्यवहार समायोजित करना
तरीका 1: ViewTreeObserver.OnGlobalLayoutListener (क्लासिक, विश्वसनीय)
रूट व्यू के आयाम परिवर्तन का पता लगाता है - कीबोर्ड दिखने पर ऊंचाई कम हो जाती है।
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
public class KeyboardVisibilityDetector {
private final View rootView;
private final OnKeyboardVisibilityListener listener;
private int previousHeight = -1;
public interface OnKeyboardVisibilityListener {
void onKeyboardVisibilityChanged(boolean isVisible, int keyboardHeight);
}
public KeyboardVisibilityDetector(View rootView, OnKeyboardVisibilityListener listener) {
this.rootView = rootView;
this.listener = listener;
}
public void start() {
rootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}
public void stop() {
rootView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
}
private final ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener = () -> {
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
int screenHeight = rootView.getRootView().getHeight();
int visibleHeight = r.bottom - r.top;
int keyboardHeight = screenHeight - visibleHeight;
// थ्रेशोल्ड: स्क्रीन ऊंचाई का 15% से अधिक = कीबोर्ड दिख रहा है
boolean isVisible = keyboardHeight > screenHeight * 0.15;
// केवल परिवर्तन पर कॉलबैक
if (keyboardHeight != previousHeight) {
previousHeight = keyboardHeight;
if (listener != null) {
listener.onKeyboardVisibilityChanged(isVisible, keyboardHeight);
}
}
};
}
// उपयोग
KeyboardVisibilityDetector detector = new KeyboardVisibilityDetector(
findViewById(android.R.id.content), // रूट व्यू
(isVisible, height) -> {
if (isVisible) {
Log.d("Keyboard", "दिख रहा है, ऊंचाई: " + height);
// UI एडजस्ट करें
} else {
Log.d("Keyboard", "छुपा हुआ");
// UI रीसेट करें
}
}
);
detector.start();
// onDestroy में
detector.stop();
तरीका 2: WindowInsets (API 20+, आधुनिक)
// Activity/Fragment में
View rootView = findViewById(android.R.id.content);
rootView.setOnApplyWindowInsetsListener((view, insets) -> {
// API 30+ (WindowInsetsCompat के लिए androidx.core:core-ktx उपयोग करें)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
android.view.WindowInsets imeInsets = insets.getInsets(android.view.WindowInsets.Type.ime());
boolean isVisible = imeInsets.bottom > 0;
int height = imeInsets.bottom;
// UI अपडेट करें
adjustUIForKeyboard(isVisible, height);
}
return insets;
});
// WindowInsetsCompat (सभी API लेवल के लिए)
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.ViewCompat;
ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> {
WindowInsetsCompat imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime());
boolean isVisible = imeInsets.bottom > 0;
int height = imeInsets.bottom;
adjustUIForKeyboard(isVisible, height);
return insets;
});
तरीका 3: WindowInsetsAnimationCallback (API 30+, एनिमेशन के साथ)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
rootView.setWindowInsetsAnimationCallback(new WindowInsetsAnimation.Callback(
WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP) {
@Override
public void onPrepare(WindowInsetsAnimation animation) {
// एनिमेशन शुरू
}
@Override
public WindowInsets onProgress(WindowInsets insets,
List runningAnimations) {
// एनिमेशन प्रगति पर
WindowInsetsCompat imeInsets = WindowInsetsCompat.toWindowInsetsCompat(insets, rootView)
.getInsets(WindowInsetsCompat.Type.ime());
boolean isVisible = imeInsets.bottom > 0;
adjustUIForKeyboard(isVisible, imeInsets.bottom);
return insets;
}
@Override
public void onEnd(WindowInsetsAnimation animation) {
// एनिमेशन समाप्त
}
});
}
पूर्ण उदाहरण: चैट UI में बॉटम बार एडजस्ट करना
activity_chat.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="https://schemas.android.com/apk/res/android"
android:id="@+id/rootView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/messagesRecyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/inputContainer"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<LinearLayout
android:id="@+id/inputContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp"
android:background="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<EditText
android:id="@+id/messageEditText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="संदेश लिखें..." />
<Button
android:id="@+id/sendButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="भेजें" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
ChatActivity.java
import android.os.Bundle;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class ChatActivity extends AppCompatActivity {
private View rootView;
private View inputContainer;
private EditText messageEditText;
private int originalBottomPadding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
rootView = findViewById(R.id.rootView);
inputContainer = findViewById(R.id.inputContainer);
messageEditText = findViewById(R.id.messageEditText);
Button sendButton = findViewById(R.id.sendButton);
originalBottomPadding = inputContainer.getPaddingBottom();
// तरीका 1: WindowInsetsCompat (अनुशंसित)
ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> {
WindowInsetsCompat imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime());
boolean isKeyboardVisible = imeInsets.bottom > 0;
int keyboardHeight = imeInsets.bottom;
adjustInputContainer(isKeyboardVisible, keyboardHeight);
return insets;
});
sendButton.setOnClickListener(v -> sendMessage());
}
private void adjustInputContainer(boolean isVisible, int keyboardHeight) {
if (isVisible) {
// कीबोर्ड के ऊपर कंटेनर रखने के लिए बॉटम पैडिंग जोड़ें
inputContainer.setPadding(
inputContainer.getPaddingLeft(),
inputContainer.getPaddingTop(),
inputContainer.getPaddingRight(),
keyboardHeight
);
} else {
// मूल पैडिंग रीस्टोर करें
inputContainer.setPadding(
inputContainer.getPaddingLeft(),
inputContainer.getPaddingTop(),
inputContainer.getPaddingRight(),
originalBottomPadding
);
}
}
private void sendMessage() {
String text = messageEditText.getText().toString().trim();
if (!text.isEmpty()) {
// संदेश भेजें लॉजिक
messageEditText.setText("");
}
}
}
मैनिफेस्ट कॉन्फ़िगरेशन
<activity
android:name=".ChatActivity"
android:windowSoftInputMode="adjustResize">
<!-- adjustResize आवश्यक है ताकि WindowInsets सही काम करे -->
</activity>
तुलना तालिका
| तरीका | न्यूनतम API | सटीकता | जटिलता | एनिमेशन सपोर्ट |
|---|---|---|---|---|
| ViewTreeObserver | 1 | अच्छी | कम | नहीं |
| WindowInsets (framework) | 20 | उत्कृष्ट | मध्यम | API 30+ |
| WindowInsetsCompat | 14+ | उत्कृष्ट | कम | API 30+ |
सर्वोत्तम अभ्यास
- WindowInsetsCompat उपयोग करें - सभी API लेवल पर काम करता है
adjustResizeमैनिफेस्ट में सेट करें- लिस्टनर को
onDestroyमें हटाएं (लीक रोकने के लिए) - थ्रेशोल्ड (15%) डिवाइस/ओरिएंटेशन के अनुसार ट्यून करें
- लैंडस्केप मोड में अलग व्यवहार हो सकता है - टेस्ट करें