English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
مقدمة
بعد فترة من التطوير، لا يمكن تجنب مواجهة جميع أنواع الحفر.
بينما في طريق تطوير Android، يمكن القول بأن "لوحة المفاتيح الافتراضية تغطي حقل الإدخال" هي حفرة كبيرة ومستمرة - هيا، دعنا نرى تدريجياً.
دليل البدء
في الحالة الأساسية، كما هو موضح في الشكل: في قاعدة الصفحة هناك EditText، إذا لم يتم اتخاذ أي إجراء، فإنه قد يغطي EditText عند ظهور لوحة المفاتيح الافتراضية.
معالجة هذه الحالة بسيطة للغاية، تحتاج فقط إلى تعيين activity في ملف AndroidManifest: قيمة android:windowSoftInputMode يجب أن تكون adjustPan أو adjustResize، مثل هذا:
<activity android:name=".MainActivity" android:windowSoftInputMode="adjustPan" > ... </activity>
عادة، يمكنهم حل المشكلة، بالطبع، هناك فروق بسيطة في تأثير adjustPan وadjustResize.
adjustPan هو رفع الواجهة بأكملها للأسفل، مما يكشف عن مربع الإدخال دون تغيير ترتيب الواجهة؛
adjustResize هو إعادة حساب حجم الواجهة بعد إطلاق لوحة المفاتيح اللغوية، مما يعني استخدام مساحة واجهة أقل لعرض المحتوى، وتبعاً لذلك فإن مربع الإدخال يكون أيضًا داخل ذلك.
↑↑↑ حسنًا، هذا مجرد مستوى البدء، بشكل عام يمكن لكل مهندس Android على الأرض معالجته.
لا تقلق، انظر أدناه~
أضف WebView واتركها تنظر؟ الثقب قد أتى...
في المقالة التعريفية أعلاه، يُفتح لوحة المفاتيح اللغوية بواسطة EditText الأصلي.
وصف الحالة
في هذه الحالة، الأمور ستكون معقدة:
أولاً، في حالة أن الصفحة ليست في الوضع الكامل، فإن تعيين adjustPan لـ activity سيكون غير فعال.
ثانيًا، في حالة الوضع الكامل للصفحة، سيفشل adjustPan وadjustResize.
—— تفسيرًا، هنا هو الوضع الكامل للنافذة، وهو أن الصفحة كاملة هي في الوضع الكامل، بما في ذلك التطبيق أو activity يستخدم موضوع Fullscreen أو يستخدم『تلوين الألوان في الحالة』،『شاشة غامقة للوضع الحالي』،『وضع غامق』، etc. - وبشكل عام، إذا قام التطبيق باتخاذ سيطرة على شريط الحالة، فإن هذا المشكلة سيحدث.
يمكن لجدول أدناه ببساطة قائمة على الحالات المحددة.
لماذا يقال أنه ثقب؟”issue 5497”
الحالة في الجدول أعلاه ليست ما يتوقعه Google، والحالة المثالية طبعاً هي أن تكون جميعها تعمل بشكل صحيح - لذا هذا في الواقع هو خطأ في نظام Android نفسه.
لماذا يقول المقال في بدايته أن هذا هو الثقب؟
—— لأن هذا الأخطاء تم تقريره من عصر Android 1.x (2009) حتى الآن لم يتم إصلاحه في Android 7.0 (2016) ...(^ω^)
يمكن القول أن هذا ليس مجرد ثقب، بل ثقب تم حفره بواسطة الشركة الرسمية~
“issue 5497”,تفاصيل النافذة ☞ Issue 5497 - android -WebView adjustResize windowSoftInputMode breaks when activity is fullscreen - Android Open Source Project - Issue Tracker - Google Project Hosting
بالطبع، مهما كان من حفر هذه الحفر، يجب على المطورين حلها في النهاية.
بعد مواجهة الحفر، هناك طريقتان يمكن أن تتجاوزها: الهروب أو الحفر.
طريقة الهروب من الحفر
كما ذكرت في المقالة السابقة، شرط ظهور الحفرة هو: استخدام activity يحتوي على WebView بنمط الشاشة الكاملة أو نمط adjustPan.
إذن، طريقة الهروب من الحفر بسيطة جداً -
إذا كان هناك WebView في activity، لا تستخدم نمط الشاشة الكاملة، وأعد تعيين قيمة windowSoftInputMode إلى adjustResize.
كيف، أليس بسيطًا؟
لكن هناك أوقات تحتاج فيها إلى الحصول على نمط الشاشة الكاملة مع WebView، في هذه الحالة، لا يمكننا الهروب من الحفر، نحتاج إلى طريقة جديدة للحفر. لحسن الحظ، الذكاء المطورين لا حدود له، هذه الحفرة موجودة منذ سنوات عديدة، وما زال هناك من وجد بعض الحلول.
AndroidBug5497Workaround
أعتقد أن أفضل حل هو هذا:AndroidBug5497Workaround، يحتاج فقط إلى فئة AndroidBug5497Workaround السحرية.
من خلال الاسم، يمكننا أن نعرف أنها مصممة خصيصًا لمكافحة مشكلة الـ”5497“، والخطوات لاستخدامها بسيطة جدًا:
أكتب فئة AndroidBug5497Workaround في المشروع
لإضافة حفرة على method onCreate لل نشاط الذي يحتاج إلى الحفر، أضف سطرًا واحدًا AndroidBug5497Workaround.assistActivity(this).
بعد الاختبار، يمكن استخدامها في جميع إصدارات Android تقريبًا، والنتيجة تقريبًا مشابهة لتلك التي يتم تعيينها كـ adjustResize.
أنظر إلى رسمة مقارنة:
من صفحة Activity الم полئية بـ WebView في نمط الشاشة الكاملة من تطبيقنا، من اليسار إلى اليمين: نمط بدون لوحة المفاتيح، تأثير لوحة المفاتيح التي تغطي حقل الإدخال، وكذلك النتيجة النهائية بعد استخدام AndroidBug5497Workaround.
ما هو مبدأها؟
فئة AndroidBug5497Workaround هذه، تبدو رائعة في الواقع ليست معقدة جدًا، تحتوي على عدة أسطر من الكود، سأضعها هنا أولاً:
public class AndroidBug5497Workaround { // لمزيد من المعلومات، انظر https://code.google.com/p/android/issues/detail?id=5497 // تستخدم هذه الفئة ببساطة عن طريق استدعاء assistActivity() على نشاط (Activity) تم تحديد واجهة المستخدم الخاص به. 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; } // 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); // في نمط الشاشة الكاملة: return r.bottom } }
يقوم الرمز بشكل عام بفعل هذه الأمور:
1. العثور على View الجذر لـ activity
لننظر إلى رمز المدخل:
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content); mChildOfContent = content.getChildAt(0);
في ذلك، يشير android.R.id.content في السطر الأول إلى View الجذر لكل Activity في واجهة المستخدم الخاصة بـ Android التي يمكن للمدربين التحكم فيها.
إذا كان Activity في نمط الشاشة الكاملة، فإن android.R.id.content يشغل منطقة الشاشة الكاملة.
如果Activity是普通的非全屏模式,那么android.R.id.content就是占满除状态栏之外的所有区域。
其他情况,如Activity是弹窗、或者7.0以后的分屏样式等,android.R.id.content也是弹窗的范围或者分屏所在的半个屏幕——这些情况较少,就暂且不考虑了。
我们经常用的setContentView(View view)/setContent(int layRes)其实就是把我们指定的View或者layRes放到android.R.id.content里面,成为它的子View。
所以,然后,第二行content.getChildAt(0)获取到的mChildOfContent,其实也就是用以获取到我们用setContentView放进去的View。
2.设置一个Listener监听View树变化
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener({ //简化了写法 possiblyResizeChildOfContent(); });
View.getViewTreeObserver()可以获取一个ViewTreeObserver对象——这个对象是一个观察者,专门用以监听当前View树所发生的一些变化。这里所注册的addOnGlobalLayoutListener,就是会在当前的View树的全局布局(GlobalLayout)发生变化、或者其中的View可视状态有变化时,进行通知回调。
——『软键盘弹出』,则是会触发这个事件的一个源。 (软键盘弹出会使GlobalLayout发生变化)
也就是说,现在能监听到『软键盘弹出』的事件了。
3.界面变化之后,获取”可用高度”
当软键盘弹出了之后,接下来的事情是获取改变之后的界面的可用高度(可以被开发者用以显示内容的高度)。
直接看代码:
private int computeUsableHeight() { Rect rect = new Rect(); mChildOfContent.getWindowVisibleDisplayFrame(rect); // rect.top其实是状态栏的高度,如果是全屏主题,直接 return rect.bottom就可以了 return (rect.bottom - rect.top); }
View.getWindowVisibleDisplayFrame(Rect rect)، هذا السطر يمكنه الحصول على Rect - وهو المربع الذي يبقى من الواجهة بعد إزالة شريط العناوين وإزالة الجزء الذي يغطيه لوحة المفاتيح، كما هو موضح في الشكل، المنطقة المبيتة بالصندوق الأحمر.
شكل منطقة Rect
يمكن أيضًا ملاحظة:
قيمة rect.top هي في الواقع ارتفاع شريط العناوين. (ويستخدم غالبًا أيضًا كطريقة للحصول على ارتفاع شريط العناوين)
ارتفاع الشاشة - rect.bottom هو ارتفاع لوحة المفاتيح. (تم أيضًا ظهور طريقة للحصول على ارتفاع لوحة المفاتيح)
في هذه الحالة، يكون لدينا:
في نمط الشاشة الكاملة، الارتفاع المتاح = rect.bottom
في نمط غير كامل الشاشة، الارتفاع المتاح = rect.bottom - rect.top
4. الخطوة الأخيرة، إعادة تعيين الارتفاع
الارتفاع المتاح الذي نحسبه هو الارتفاع الذي يمكن رؤيته بصريًا في الواجهة حاليًا. ولكن الارتفاع الفعلي للواجهة حاليًا هو أكبر من الارتفاع المتاح بفارق مسافة لوحة المفاتيح.
إذاً، الخطوة الأخيرة، هي تعيين طول الواجهة إلى الارتفاع المتاح - تم الانتهاء من العمل.
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; } // keyboard probably just became hidden frameLayoutParams.height = usableHeightSansKeyboard; } mChildOfContent.requestLayout(); usableHeightPrevious = usableHeightNow; } }
تم إضافة�断ة "heightDifference > (usableHeightSansKeyboard/4)" في الكود الموجود أعلاه، هذا لاستبعاد التداخل غير الضروري. لأن هناك العديد من الأسباب التي يمكن أن تؤدي إلى إطلاق حدث OnGlobalLayout، ليس فقط التغيير في إطلاق لوحة المفاتيح الافتراضية، بل أيضًا التغييرات في العروض الفرعية للوحة، وهي لها تأثير محدود على طول الشاشة. بعد إضافة هذه الحدثة، سيتم إعادة تعيين الارتفاع فقط إذا تغير الارتفاع للشاشة أكثر من ربع الارتفاع، مما يضمن أن يتم استجابة الكود فقط عند إطلاق لوحة المفاتيح الافتراضية.
الخلاصة
بشكل مختصر، يمكن تلخيصها كما يلي:
Activity عادي (بدون WebView)، استخدم adjustpan أو adjustResize مباشرة.
إذا كان يحتوي على WebView:
أ) إذا لم يكن الوضع كاملاً، يمكنك استخدام adjustResize.
ب) إذا كان الوضع كاملاً، استخدم AndroidBug5497Workaround للمعالجة.
ما تم ذكره أعلاه هو الحل النهائي الذي قدمته لكم مجلة Android لتجنب عائق لوحة المفاتيح الافتراضية، آمل أن يكون مفيداً لكم، إذا كان لديكم أي استفسارات، يرجى ترك تعليق، وسأقوم بالرد على رسائلكم في أقرب وقت ممكن. وأعرب عن تقديري الشديد لدعمكم لموقع呐喊 لتعليم البرمجة!
البيان: محتويات هذا المقال تم جمعها من الإنترنت، ملكية المحتويات للمالك الأصلي، تم جمع المحتويات من قبل المستخدمين عبر الإنترنت بشكل متعاوني وتحميلها بشكل مستقل، هذا الموقع لا يمتلك حقوق الملكية، لم يتم تعديل المحتويات بشكل يدوي، ولا يتحمل أي مسؤولية قانونية. إذا كنت قد وجدت محتوى يشتبه في انتهاك حقوق النسخ، فالرجاء إرسال بريد إلكتروني إلى: notice#oldtoolbag.com (عند إرسال البريد الإلكتروني، يرجى استبدال # ب @) لإبلاغنا، يرجى تقديم الأدلة ذات الصلة، وإذا تم التحقق من ذلك، سيتم حذف المحتوى المشبوه فوراً.