Tôi muốn thay đổi bố cục dựa trên việc bàn phím ảo có được hiển thị hay không. Tôi đã tìm kiếm API và các blog khác nhau nhưng dường như không thể tìm thấy bất cứ điều gì hữu ích.
Có thể không?
Cảm ơn!
Tôi muốn thay đổi bố cục dựa trên việc bàn phím ảo có được hiển thị hay không. Tôi đã tìm kiếm API và các blog khác nhau nhưng dường như không thể tìm thấy bất cứ điều gì hữu ích.
Có thể không?
Cảm ơn!
Câu trả lời:
Giải pháp này sẽ không hoạt động đối với bàn phím mềm và
onConfigurationChanged
sẽ không được gọi cho bàn phím mềm (ảo).
Bạn phải tự xử lý các thay đổi cấu hình.
http://developer.android.com/guide/topics/resource/r nb-changes.html#HandlingTheChange
Mẫu vật:
// from the link above
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks whether a hardware keyboard is available
if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
Toast.makeText(this, "keyboard visible", Toast.LENGTH_SHORT).show();
} else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
Toast.makeText(this, "keyboard hidden", Toast.LENGTH_SHORT).show();
}
}
Sau đó, chỉ cần thay đổi mức độ hiển thị của một số chế độ xem, cập nhật một trường và thay đổi tệp bố cục của bạn.
newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
~ Chris
Đây có thể không phải là giải pháp hiệu quả nhất. Nhưng điều này làm việc cho tôi mỗi lần ... Tôi gọi chức năng này ở bất cứ nơi nào tôi cần để nghe phần mềm.
boolean isOpened = false;
public void setListenerToRootView() {
final View activityRootView = getWindow().getDecorView().findViewById(android.R.id.content);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
if (heightDiff > 100) { // 99% of the time the height diff will be due to a keyboard.
Toast.makeText(getApplicationContext(), "Gotcha!!! softKeyboardup", 0).show();
if (isOpened == false) {
//Do two things, make the view top visible and the editText smaller
}
isOpened = true;
} else if (isOpened == true) {
Toast.makeText(getApplicationContext(), "softkeyborad Down!!!", 0).show();
isOpened = false;
}
}
});
}
Lưu ý: Cách tiếp cận này sẽ gây ra vấn đề nếu người dùng sử dụng bàn phím nổi.
android:windowSoftInputMode="adjustPan"
hoặc adjustResize
với cửa sổ toàn màn hình, vì bố cục không bao giờ được thay đổi kích thước.
Nếu bạn muốn xử lý hiển thị / ẩn cửa sổ bàn phím IMM (ảo) khỏi Hoạt động của mình, bạn sẽ cần phân lớp bố cục và ghi đè phương thức onMesure (để bạn có thể xác định chiều rộng đo được và chiều cao đo được của bố cục). Sau đó, đặt bố cục được phân lớp làm chế độ xem chính cho Hoạt động của bạn bằng setContentView (). Bây giờ bạn sẽ có thể xử lý các sự kiện hiển thị / ẩn cửa sổ IMM. Nếu điều này nghe có vẻ phức tạp, thì nó không thực sự như vậy. Đây là mã:
tệp chính
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<EditText
android:id="@+id/SearchText"
android:text=""
android:inputType="text"
android:layout_width="fill_parent"
android:layout_height="34dip"
android:singleLine="True"
/>
<Button
android:id="@+id/Search"
android:layout_width="60dip"
android:layout_height="34dip"
android:gravity = "center"
/>
</LinearLayout>
Bây giờ bên trong Activity khai báo lớp con cho bố cục của bạn (main.xml)
public class MainSearchLayout extends LinearLayout {
public MainSearchLayout(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.main, this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d("Search Layout", "Handling Keyboard Window shown");
final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
final int actualHeight = getHeight();
if (actualHeight > proposedheight){
// Keyboard is shown
} else {
// Keyboard is hidden
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Bạn có thể thấy từ mã mà chúng tôi tăng bố cục cho Hoạt động của chúng tôi trong hàm tạo của lớp con
inflater.inflate(R.layout.main, this);
Và bây giờ chỉ cần đặt chế độ xem nội dung của bố cục được phân lớp cho Hoạt động của chúng tôi.
public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MainSearchLayout searchLayout = new MainSearchLayout(this, null);
setContentView(searchLayout);
}
// rest of the Activity code and subclassed layout...
}
android:windowSoftInputMode="adjustPan"
hoặc adjustResize
với cửa sổ toàn màn hình, vì bố cục không bao giờ được thay đổi kích thước.
Tôi đã làm theo cách này:
Thêm OnKeyboardVisibilityListener
giao diện.
public interface OnKeyboardVisibilityListener {
void onVisibilityChanged(boolean visible);
}
HomeActivity.java :
public class HomeActivity extends Activity implements OnKeyboardVisibilityListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_up);
// Other stuff...
setKeyboardVisibilityListener(this);
}
private void setKeyboardVisibilityListener(final OnKeyboardVisibilityListener onKeyboardVisibilityListener) {
final View parentView = ((ViewGroup) findViewById(android.R.id.content)).getChildAt(0);
parentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
private boolean alreadyOpen;
private final int defaultKeyboardHeightDP = 100;
private final int EstimatedKeyboardDP = defaultKeyboardHeightDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);
private final Rect rect = new Rect();
@Override
public void onGlobalLayout() {
int estimatedKeyboardHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, parentView.getResources().getDisplayMetrics());
parentView.getWindowVisibleDisplayFrame(rect);
int heightDiff = parentView.getRootView().getHeight() - (rect.bottom - rect.top);
boolean isShown = heightDiff >= estimatedKeyboardHeight;
if (isShown == alreadyOpen) {
Log.i("Keyboard state", "Ignoring global layout change...");
return;
}
alreadyOpen = isShown;
onKeyboardVisibilityListener.onVisibilityChanged(isShown);
}
});
}
@Override
public void onVisibilityChanged(boolean visible) {
Toast.makeText(HomeActivity.this, visible ? "Keyboard is active" : "Keyboard is Inactive", Toast.LENGTH_SHORT).show();
}
}
Hy vọng điều này sẽ giúp bạn.
Dựa trên Mã từ Nebojsa Tomcic, tôi đã phát triển RelativeLayout-Subgroup sau đây:
import java.util.ArrayList;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
public class KeyboardDetectorRelativeLayout extends RelativeLayout {
public interface IKeyboardChanged {
void onKeyboardShown();
void onKeyboardHidden();
}
private ArrayList<IKeyboardChanged> keyboardListener = new ArrayList<IKeyboardChanged>();
public KeyboardDetectorRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public KeyboardDetectorRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public KeyboardDetectorRelativeLayout(Context context) {
super(context);
}
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();
} else if (actualHeight < proposedheight) {
notifyKeyboardHidden();
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void notifyKeyboardHidden() {
for (IKeyboardChanged listener : keyboardListener) {
listener.onKeyboardHidden();
}
}
private void notifyKeyboardShown() {
for (IKeyboardChanged listener : keyboardListener) {
listener.onKeyboardShown();
}
}
}
Điều này hoạt động khá tốt ... Đánh dấu, rằng giải pháp này sẽ chỉ hoạt động khi Chế độ nhập mềm của Hoạt động của bạn được đặt thành "WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE"
android:windowSoftInputMode="adjustPan"
hoặc adjustResize
với cửa sổ toàn màn hình, vì bố cục không bao giờ được thay đổi kích thước.
Giống như câu trả lời của @ amalBit, hãy đăng ký một người nghe để bố trí toàn cầu và tính toán sự khác biệt của đáy có thể nhìn thấy của dectorView và đáy được đề xuất của nó, nếu chênh lệch lớn hơn một số giá trị (đoán chiều cao của IME), chúng tôi nghĩ IME đã tăng:
final EditText edit = (EditText) findViewById(R.id.edittext);
edit.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (keyboardShown(edit.getRootView())) {
Log.d("keyboard", "keyboard UP");
} else {
Log.d("keyboard", "keyboard Down");
}
}
});
private boolean keyboardShown(View rootView) {
final int softKeyboardHeight = 100;
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
int heightDiff = rootView.getBottom() - r.bottom;
return heightDiff > softKeyboardHeight * dm.density;
}
ngưỡng chiều cao 100 là chiều cao tối thiểu được đoán của IME.
Điều này hoạt động cho cả điều chỉnhPan và điều chỉnhResize.
Giải pháp của Nebojsa gần như làm việc với tôi. Khi tôi nhấp vào bên trong EditText nhiều dòng, nó biết bàn phím đã được hiển thị, nhưng khi tôi bắt đầu nhập vào bên trong EditText, thì factHeight và ProHeight vẫn giống nhau nên không biết bàn phím của chúng vẫn hiển thị. Tôi đã thực hiện một sửa đổi nhỏ để lưu trữ chiều cao tối đa và nó hoạt động tốt. Đây là lớp con được sửa đổi:
public class CheckinLayout extends RelativeLayout {
private int largestHeight;
public CheckinLayout(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.checkin, this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
largestHeight = Math.max(largestHeight, getHeight());
if (largestHeight > proposedheight)
// Keyboard is shown
else
// Keyboard is hidden
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Không chắc chắn nếu có ai đăng bài này. Tìm thấy giải pháp này đơn giản để sử dụng! . Lớp SoftPal nằm trên gist.github.com . Nhưng trong khi bật lên bàn phím / ẩn cuộc gọi lại sự kiện, chúng ta cần một trình xử lý để thực hiện đúng cách trên UI:
/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use your root layout
InputMethodManager im = (InputMethodManager) getSystemService(Service.INPUT_METHOD_SERVICE);
/*
Instantiate and pass a callback
*/
SoftKeyboard softKeyboard;
softKeyboard = new SoftKeyboard(mainLayout, im);
softKeyboard.setSoftKeyboardCallback(new SoftKeyboard.SoftKeyboardChanged()
{
@Override
public void onSoftKeyboardHide()
{
// Code here
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// Code here will run in UI thread
...
}
});
}
@Override
public void onSoftKeyboardShow()
{
// Code here
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// Code here will run in UI thread
...
}
});
}
});
Tôi giải quyết điều này bằng cách ghi đè onKeyPreIme (int keyCode, sự kiện KeyEvent) trong EditText tùy chỉnh của tôi.
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
//keyboard will be hidden
}
}
Tôi có một loại hack để làm điều này. Mặc dù dường như không có cách nào để phát hiện khi bàn phím mềm đã hiển thị hoặc bị ẩn, nhưng thực tế bạn có thể phát hiện khi nào nó sắp được hiển thị hoặc ẩn bằng cách đặt OnFocusChangeListener
trên bàn phím EditText
mà bạn đang nghe.
EditText et = (EditText) findViewById(R.id.et);
et.setOnFocusChangeListener(new View.OnFocusChangeListener()
{
@Override
public void onFocusChange(View view, boolean hasFocus)
{
//hasFocus tells us whether soft keyboard is about to show
}
});
LƯU Ý: Một điều cần lưu ý với bản hack này là cuộc gọi lại này được kích hoạt ngay lập tức khi EditText
tăng hoặc giảm trọng tâm. Điều này thực sự sẽ kích hoạt ngay trước khi bàn phím mềm hiển thị hoặc ẩn. Cách tốt nhất mà tôi đã tìm thấy để làm một cái gì đó sau khi bàn phím hiển thị hoặc ẩn là sử dụng Handler
và trì hoãn một cái gì đó ~ 400ms, như vậy:
EditText et = (EditText) findViewById(R.id.et);
et.setOnFocusChangeListener(new View.OnFocusChangeListener()
{
@Override
public void onFocusChange(View view, boolean hasFocus)
{
new Handler().postDelayed(new Runnable()
{
@Override
public void run()
{
//do work here
}
}, 400);
}
});
OnFocusChangeListener
chỉ cho biết EditText
có tập trung sau khi nhà nước thay đổi. Nhưng IME
có thể bị ẩn khi EditText
có trọng tâm, làm thế nào để phát hiện trường hợp này?
Sander, tôi tin rằng bạn đang cố gắng hiển thị chế độ xem bị chặn bởi bàn phím mềm. Hãy thử http://android-developers.blogspot.com/2009/04/updating-appluggest-for-on-screen.html .
Tôi đã giải quyết vấn đề về mã hóa textview dòng đơn.
package com.helpingdoc;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
public class MainSearchLayout extends LinearLayout {
int hieght = 0;
public MainSearchLayout(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.main, this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d("Search Layout", "Handling Keyboard Window shown");
if(getHeight()>hieght){
hieght = getHeight();
}
final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
final int actualHeight = getHeight();
System.out.println("....hieght = "+ hieght);
System.out.println("....actualhieght = "+ actualHeight);
System.out.println("....proposedheight = "+ proposedheight);
if (actualHeight > proposedheight){
// Keyboard is shown
} else if(actualHeight<proposedheight){
// Keyboard is hidden
}
if(proposedheight == hieght){
// Keyboard is hidden
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Bạn cũng có thể kiểm tra phần đệm dưới cùng đầu tiên của DecorView. Nó sẽ được đặt thành giá trị khác không khi bàn phím được hiển thị.
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
View view = getRootView();
if (view != null && (view = ((ViewGroup) view).getChildAt(0)) != null) {
setKeyboardVisible(view.getPaddingBottom() > 0);
}
super.onLayout(changed, left, top, right, bottom);
}
Ẩn | Hiển thị sự kiện cho bàn phím có thể được nghe thông qua hack đơn giản trong OnGlobalLayoutListener:
final View activityRootView = findViewById(R.id.top_root);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
if (heightDiff > 100) {
// keyboard is up
} else {
// keyboard is down
}
}
});
Ở đây ActivityRootView là chế độ xem gốc của Activity.
sử dụng viewTreeObserver để dễ dàng lấy sự kiện bàn phím.
layout_parent.viewTreeObserver.addOnGlobalLayoutListener {
val r = Rect()
layout_parent.getWindowVisibleDisplayFrame(r)
if (layout_parent.rootView.height - (r.bottom - r.top) > 100) { // if more than 100 pixels, its probably a keyboard...
Log.e("TAG:", "keyboard open")
} else {
Log.e("TAG:", "keyboard close")
}
}
** layout_parent là chế độ xem của bạn nhưedit_text.parent
Câu trả lời của Nebojsa Tomcic không hữu ích cho tôi. Tôi có RelativeLayout
bằng TextView
và AutoCompleteTextView
bên trong nó. Tôi cần cuộn TextView
xuống phía dưới khi bàn phím được hiển thị và khi nó bị ẩn. Để thực hiện điều này, tôi onLayout
áp dụng phương pháp này và nó hoạt động tốt với tôi.
public class ExtendedLayout extends RelativeLayout
{
public ExtendedLayout(Context context, AttributeSet attributeSet)
{
super(context, attributeSet);
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.main, this);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
super.onLayout(changed, l, t, r, b);
if (changed)
{
int scrollEnd = (textView.getLineCount() - textView.getHeight() /
textView.getLineHeight()) * textView.getLineHeight();
textView.scrollTo(0, scrollEnd);
}
}
}