रनटाइम रिसोर्स ओवरले (आरआरओ) का उपयोग करने के बजाय कार यूआई लाइब्रेरी में घटक अनुकूलन का पूर्ण कार्यान्वयन बनाने के लिए कार यूआई लाइब्रेरी प्लगइन्स का उपयोग करें। आरआरओ आपको कार यूआई लाइब्रेरी घटकों के केवल एक्सएमएल संसाधनों को बदलने में सक्षम बनाता है, जो आपके द्वारा अनुकूलित किए जाने की सीमा को सीमित करता है।
एक प्लगइन बनाएं
कार यूआई लाइब्रेरी प्लगइन एक एपीके है जिसमें ऐसी कक्षाएं शामिल हैं जो प्लगइन एपीआई के एक सेट को लागू करती हैं। प्लगइन एपीआई को एक स्थिर लाइब्रेरी के रूप में प्लगइन में संकलित किया जा सकता है।
सूंग और ग्रैडल में उदाहरण देखें:
सूंग
इस सूंग उदाहरण पर विचार करें:
android_app {
name: "my-plugin",
min_sdk_version: "28",
target_sdk_version: "30",
aaptflags: ["--shared-lib"],
sdk_version: "current",
manifest: "src/main/AndroidManifest.xml",
srcs: ["src/main/java/**/*.java"],
resource_dirs: ["src/main/res"],
static_libs: [
"car-ui-lib-oem-apis",
],
// Disable optimization is mandatory to prevent R.java class from being
// stripped out
optimize: {
enabled: false,
},
certificate: ":my-plugin-certificate",
}
ग्रैडल
यह build.gradle
फ़ाइल देखें:
apply plugin: 'com.android.application'
android {
compileSdkVersion 30
defaultConfig {
minSdkVersion 28
targetSdkVersion 30
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
signingConfigs {
debug {
storeFile file('chassis_upload_key.jks')
storePassword 'chassis'
keyAlias 'chassis'
keyPassword 'chassis'
}
}
}
dependencies {
implementation project(':oem-apis')
// Or use the following if you'd like to use the maven artifact
// implementation 'com.android.car.ui:car-ui-lib-plugin-apis:1.0.0'
}
Settings.gradle
:
// You can remove the ':oem-apis' if you're using the maven artifact.
include ':oem-apis'
project(':oem-apis').projectDir = new File('./path/to/oem-apis')
include ':my-plugin'
project(':my-plugin').projectDir = new File('./my-plugin')
प्लगइन के मेनिफेस्ट में एक सामग्री प्रदाता घोषित होना चाहिए जिसमें निम्नलिखित विशेषताएं हों:
android:authorities="com.android.car.ui.plugin"
android:enabled="true"
android:exported="true"
android:authorities="com.android.car.ui.plugin"
प्लगइन को कार यूआई लाइब्रेरी में खोजने योग्य बनाता है। प्रदाता को निर्यात करना होगा ताकि रनटाइम पर उससे पूछताछ की जा सके। साथ ही, यदि enabled
विशेषता false
पर सेट है तो प्लगइन कार्यान्वयन के बजाय डिफ़ॉल्ट कार्यान्वयन का उपयोग किया जाएगा। सामग्री प्रदाता वर्ग का अस्तित्व आवश्यक नहीं है। ऐसी स्थिति में, प्रदाता परिभाषा में tools:ignore="MissingClass"
जोड़ना सुनिश्चित करें। नीचे नमूना मेनिफेस्ट प्रविष्टि देखें:
<application>
<provider
android:name="com.android.car.ui.plugin.PluginNameProvider"
android:authorities="com.android.car.ui.plugin"
android:enabled="false"
android:exported="true"
tools:ignore="MissingClass"/>
</application>
अंत में, सुरक्षा उपाय के रूप में, अपने ऐप पर हस्ताक्षर करें ।
एक साझा लाइब्रेरी के रूप में प्लगइन्स
एंड्रॉइड स्टैटिक लाइब्रेरीज़ के विपरीत, जिन्हें सीधे ऐप्स में संकलित किया जाता है, एंड्रॉइड साझा लाइब्रेरीज़ को एक स्टैंडअलोन एपीके में संकलित किया जाता है जिसे रनटाइम पर अन्य ऐप्स द्वारा संदर्भित किया जाता है।
एंड्रॉइड साझा लाइब्रेरी के रूप में कार्यान्वित किए गए प्लगइन्स की कक्षाएं स्वचालित रूप से ऐप्स के बीच साझा क्लासलोडर में जुड़ जाती हैं। जब कोई ऐप जो कार यूआई लाइब्रेरी का उपयोग करता है, प्लगइन साझा लाइब्रेरी पर रनटाइम निर्भरता निर्दिष्ट करता है, तो इसका क्लासलोडर प्लगइन साझा लाइब्रेरी की कक्षाओं तक पहुंच सकता है। सामान्य एंड्रॉइड ऐप्स (साझा लाइब्रेरी नहीं) के रूप में लागू किए गए प्लगइन्स ऐप कोल्ड स्टार्ट टाइम पर नकारात्मक प्रभाव डाल सकते हैं।
साझा पुस्तकालयों को लागू करें और बनाएं
एंड्रॉइड साझा लाइब्रेरीज़ के साथ विकास करना कुछ महत्वपूर्ण अंतरों के साथ सामान्य एंड्रॉइड ऐप्स की तरह ही है।
- अपने प्लगइन के ऐप मेनिफेस्ट में प्लगइन पैकेज नाम के साथ
application
टैग के अंतर्गतlibrary
टैग का उपयोग करें:
<application>
<library android:name="com.chassis.car.ui.plugin" />
...
</application>
- अपने Soong
android_app
बिल्ड नियम (Android.bp
) को AAPT ध्वजshared-lib
के साथ कॉन्फ़िगर करें, जिसका उपयोग साझा लाइब्रेरी बनाने के लिए किया जाता है:
android_app {
...
aaptflags: ["--shared-lib"],
...
}
साझा पुस्तकालयों पर निर्भरता
सिस्टम पर प्रत्येक ऐप के लिए जो कार यूआई लाइब्रेरी का उपयोग करता है, प्लगइन पैकेज नाम के साथ application
टैग के तहत ऐप मेनिफेस्ट में uses-library
टैग शामिल करें:
<manifest>
<application
android:name=".MyApp"
...>
<uses-library android:name="com.chassis.car.ui.plugin" android:required="false"/>
...
</application>
</manifest>
एक प्लगइन स्थापित करें
PRODUCT_PACKAGES
में मॉड्यूल को शामिल करके प्लगइन्स को सिस्टम विभाजन पर पूर्वस्थापित किया जाना चाहिए। पहले से इंस्टॉल किए गए पैकेज को किसी अन्य इंस्टॉल किए गए ऐप की तरह ही अपडेट किया जा सकता है।
यदि आप सिस्टम पर किसी मौजूदा प्लगइन को अपडेट कर रहे हैं, तो उस प्लगइन का उपयोग करने वाला कोई भी ऐप स्वचालित रूप से बंद हो जाता है। उपयोगकर्ता द्वारा दोबारा खोले जाने पर, उनके पास अद्यतन परिवर्तन होते हैं। यदि ऐप नहीं चल रहा था, तो अगली बार जब यह शुरू होगा तो इसमें अपडेटेड प्लगइन होगा।
एंड्रॉइड स्टूडियो के साथ प्लगइन इंस्टॉल करते समय, कुछ अतिरिक्त बातों का ध्यान रखना चाहिए। लेखन के समय, एंड्रॉइड स्टूडियो ऐप इंस्टॉलेशन प्रक्रिया में एक बग है जिसके कारण प्लगइन के अपडेट प्रभावी नहीं हो पाते हैं। इसे प्लगइन के बिल्ड कॉन्फ़िगरेशन में हमेशा पैकेज मैनेजर के साथ इंस्टॉल करें (एंड्रॉइड 11 और बाद के संस्करणों पर अनुकूलन को अक्षम करता है) विकल्प का चयन करके ठीक किया जा सकता है।
इसके अलावा, प्लगइन इंस्टॉल करते समय, एंड्रॉइड स्टूडियो एक त्रुटि रिपोर्ट करता है कि उसे लॉन्च करने के लिए एक मुख्य गतिविधि नहीं मिल रही है। यह अपेक्षित है, क्योंकि प्लगइन में कोई गतिविधि नहीं है (किसी इरादे को हल करने के लिए उपयोग किए जाने वाले खाली इरादे को छोड़कर)। त्रुटि को खत्म करने के लिए, बिल्ड कॉन्फ़िगरेशन में लॉन्च विकल्प को कुछ भी नहीं में बदलें।
चित्र 1. प्लगइन एंड्रॉइड स्टूडियो कॉन्फ़िगरेशन
प्रॉक्सी प्लगइन
कार यूआई लाइब्रेरी का उपयोग करके ऐप्स के अनुकूलन के लिए एक आरआरओ की आवश्यकता होती है जो संशोधित किए जाने वाले प्रत्येक विशिष्ट ऐप को लक्षित करता है, जिसमें सभी ऐप्स में अनुकूलन समान होने पर भी शामिल है। इसका मतलब है कि प्रति ऐप एक आरआरओ आवश्यक है। देखें कि कौन से ऐप्स कार यूआई लाइब्रेरी का उपयोग करते हैं।
कार यूआई लाइब्रेरी प्रॉक्सी प्लगइन एक उदाहरण प्लगइन साझा लाइब्रेरी है जो अपने घटक कार्यान्वयन को कार यूआई लाइब्रेरी के स्थिर संस्करण में सौंपती है। इस प्लगइन को आरआरओ के साथ लक्षित किया जा सकता है, जिसका उपयोग उन ऐप्स के लिए अनुकूलन के एकल बिंदु के रूप में किया जा सकता है जो कार्यात्मक प्लगइन को लागू करने की आवश्यकता के बिना कार यूआई लाइब्रेरी का उपयोग करते हैं। आरआरओ के बारे में अधिक जानकारी के लिए, रनटाइम पर ऐप के संसाधनों का मूल्य बदलें देखें।
प्रॉक्सी प्लगइन एक प्लगइन का उपयोग करके अनुकूलन करने के लिए केवल एक उदाहरण और शुरुआती बिंदु है। आरआरओ से परे अनुकूलन के लिए, कोई प्लगइन घटकों का एक सबसेट लागू कर सकता है और बाकी के लिए प्रॉक्सी प्लगइन का उपयोग कर सकता है, या सभी प्लगइन घटकों को पूरी तरह से स्क्रैच से लागू कर सकता है।
हालाँकि प्रॉक्सी प्लगइन ऐप्स के लिए आरआरओ अनुकूलन का एक बिंदु प्रदान करता है, जो ऐप्स प्लगइन का उपयोग करने से ऑप्ट-आउट करते हैं उन्हें अभी भी एक आरआरओ की आवश्यकता होगी जो सीधे ऐप को ही लक्षित करता है।
प्लगइन एपीआई लागू करें
प्लगइन का मुख्य प्रवेश बिंदु com.android.car.ui.plugin.PluginVersionProviderImpl
वर्ग है। सभी प्लगइन्स में इस सटीक नाम और पैकेज नाम के साथ एक क्लास शामिल होनी चाहिए। इस क्लास में एक डिफ़ॉल्ट कंस्ट्रक्टर होना चाहिए और PluginVersionProviderOEMV1
इंटरफ़ेस लागू करना चाहिए।
CarUi प्लगइन्स को उन ऐप्स के साथ काम करना चाहिए जो प्लगइन से पुराने या नए हैं। इसे सुविधाजनक बनाने के लिए, सभी प्लगइन एपीआई को उनके क्लासनाम के अंत में V#
के साथ संस्करणित किया गया है। यदि कार यूआई लाइब्रेरी का नया संस्करण नई सुविधाओं के साथ जारी किया जाता है, तो वे घटक के V2
संस्करण का हिस्सा हैं। कार यूआई लाइब्रेरी पुराने प्लगइन घटक के दायरे में नई सुविधाओं को काम करने की पूरी कोशिश करती है। उदाहरण के लिए, टूलबार में एक नए प्रकार के बटन को MenuItems
में परिवर्तित करके।
हालाँकि, कार यूआई लाइब्रेरी के पुराने संस्करण वाला ऐप नए एपीआई के खिलाफ लिखे गए नए प्लगइन को अनुकूलित नहीं कर सकता है। इस समस्या को हल करने के लिए, हम प्लगइन्स को ऐप्स द्वारा समर्थित OEM एपीआई के संस्करण के आधार पर स्वयं के विभिन्न कार्यान्वयन वापस करने की अनुमति देते हैं।
PluginVersionProviderOEMV1
में एक विधि है:
Object getPluginFactory(int maxVersion, Context context, String packageName);
यह विधि एक ऑब्जेक्ट लौटाती है जो प्लगइन द्वारा समर्थित PluginFactoryOEMV#
के उच्चतम संस्करण को लागू करती है, जबकि अभी भी maxVersion
से कम या उसके बराबर है। यदि किसी प्लगइन में इतने पुराने PluginFactory
का कार्यान्वयन नहीं है, तो यह null
वापस आ सकता है, ऐसी स्थिति में CarUi घटकों के स्थिर रूप से जुड़े कार्यान्वयन का उपयोग किया जाता है।
स्थिर कार यूआई लाइब्रेरी के पुराने संस्करणों के विरुद्ध संकलित ऐप्स के साथ बैकवर्ड संगतता बनाए रखने के लिए, आपके प्लगइन के PluginVersionProvider
वर्ग के कार्यान्वयन के भीतर 2, 5 और उच्चतर के maxVersion
s का समर्थन करने की अनुशंसा की जाती है। संस्करण 1, 3, और 4 समर्थित नहीं हैं। अधिक जानकारी के लिए, PluginVersionProviderImpl
देखें।
PluginFactory
वह इंटरफ़ेस है जो अन्य सभी CarUi घटकों को बनाता है। यह यह भी परिभाषित करता है कि उनके इंटरफ़ेस के किस संस्करण का उपयोग किया जाना चाहिए। यदि प्लगइन इनमें से किसी भी घटक को कार्यान्वित नहीं करना चाहता है, तो यह उनके निर्माण फ़ंक्शन में null
वापस आ सकता है (टूलबार के अपवाद के साथ, जिसमें एक अलग customizesBaseLayout()
फ़ंक्शन है)।
pluginFactory
सीमित करती है कि CarUi घटकों के किन संस्करणों को एक साथ उपयोग किया जा सकता है। उदाहरण के लिए, ऐसी कोई pluginFactory
कभी नहीं होगी जो Toolbar
का संस्करण 100 और RecyclerView
का संस्करण 1 भी बना सके, क्योंकि इस बात की बहुत कम गारंटी होगी कि घटकों के विभिन्न प्रकार के संस्करण एक साथ काम करेंगे। टूलबार संस्करण 100 का उपयोग करने के लिए, डेवलपर्स से pluginFactory
के एक संस्करण का कार्यान्वयन प्रदान करने की अपेक्षा की जाती है जो एक टूलबार संस्करण 100 बनाता है, जो फिर बनाए जा सकने वाले अन्य घटकों के संस्करणों पर विकल्पों को सीमित करता है। अन्य घटकों के संस्करण समान नहीं हो सकते हैं, उदाहरण के लिए एक pluginFactoryOEMV100
एक ToolbarControllerOEMV100
और एक RecyclerViewOEMV70
बना सकता है।
उपकरण पट्टी
आधार लेआउट
टूलबार और "बेस लेआउट" बहुत निकट से संबंधित हैं, इसलिए टूलबार बनाने वाले फ़ंक्शन को installBaseLayoutAround
कहा जाता है। बेस लेआउट एक अवधारणा है जो टूलबार को ऐप की सामग्री के आसपास कहीं भी स्थित करने की अनुमति देता है, ऐप के ऊपर/नीचे, किनारों के साथ लंबवत, या यहां तक कि पूरे ऐप को घेरने वाले एक गोलाकार टूलबार के लिए टूलबार की अनुमति देता है। यह टूलबार/बेस लेआउट को लपेटने के लिए installBaseLayoutAround
पर एक दृश्य पास करके पूरा किया जाता है।
प्लगइन को दिए गए दृश्य को लेना चाहिए, उसे अपने पैरेंट से अलग करना चाहिए, प्लगइन के स्वयं के लेआउट को पैरेंट के समान इंडेक्स में और उसी LayoutParams
के साथ उस दृश्य के साथ फुलाना चाहिए जो अभी अलग किया गया था, और फिर दृश्य को उस लेआउट के अंदर कहीं फिर से जोड़ना चाहिए जो कि था बस फुलाया हुआ. यदि ऐप द्वारा अनुरोध किया गया है, तो फुलाए गए लेआउट में टूलबार शामिल होगा।
ऐप टूलबार के बिना बेस लेआउट का अनुरोध कर सकता है। यदि ऐसा होता है, तो installBaseLayoutAround
शून्य वापस आना चाहिए। अधिकांश प्लगइन्स के लिए, बस इतना ही होना आवश्यक है, लेकिन यदि प्लगइन लेखक ऐप के किनारे के चारों ओर एक सजावट लागू करना चाहता है, तो यह अभी भी बेस लेआउट के साथ किया जा सकता है। ये सजावट गैर-आयताकार स्क्रीन वाले उपकरणों के लिए विशेष रूप से उपयोगी हैं, क्योंकि वे ऐप को आयताकार स्थान में धकेल सकते हैं और गैर-आयताकार स्थान में स्वच्छ बदलाव जोड़ सकते हैं।
installBaseLayoutAround
को एक Consumer<InsetsOEMV1>
भी पारित किया गया है। इस उपभोक्ता का उपयोग ऐप को यह बताने के लिए किया जा सकता है कि प्लगइन ऐप की सामग्री को आंशिक रूप से कवर कर रहा है (टूलबार के साथ या अन्यथा)। ऐप तब इस स्थान पर ड्राइंग बनाए रखना जानता होगा, लेकिन किसी भी महत्वपूर्ण उपयोगकर्ता-इंटरैक्टेबल घटकों को इससे बाहर रखेगा। इस प्रभाव का उपयोग हमारे संदर्भ डिज़ाइन में टूलबार को अर्ध-पारदर्शी बनाने और उसके नीचे सूचियाँ स्क्रॉल करने के लिए किया जाता है। यदि यह सुविधा लागू नहीं की गई थी, तो सूची में पहला आइटम टूलबार के नीचे अटका रहेगा और क्लिक करने योग्य नहीं होगा। यदि इस प्रभाव की आवश्यकता नहीं है, तो प्लगइन उपभोक्ता को अनदेखा कर सकता है।
चित्र 2. टूलबार के नीचे स्क्रॉल की गई सामग्री
ऐप के परिप्रेक्ष्य से, जब प्लगइन नए इनसेट भेजता है, तो यह उन्हें InsetsChangedListener
लागू करने वाली किसी भी गतिविधि या टुकड़े से प्राप्त करेगा। यदि कोई गतिविधि या टुकड़ा InsetsChangedListener
लागू नहीं करता है, तो कार Ui लाइब्रेरी खंड Activity
या FragmentActivity
में पैडिंग के रूप में इनसेट को लागू करके डिफ़ॉल्ट रूप से इनसेट को संभाल लेगी। लाइब्रेरी डिफॉल्ट रूप से इनसेट को टुकड़ों पर लागू नहीं करती है। यहां कार्यान्वयन का एक नमूना स्निपेट है जो इनसेट को ऐप में RecyclerView
पर पैडिंग के रूप में लागू करता है:
public class MainActivity extends Activity implements InsetsChangedListener {
@Override
public void onCarUiInsetsChanged(Insets insets) {
CarUiRecyclerView rv = requireViewById(R.id.recyclerview);
rv.setPadding(insets.getLeft(), insets.getTop(),
insets.getRight(), insets.getBottom());
}
}
अंत में, प्लगइन को एक fullscreen
संकेत दिया जाता है, जिसका उपयोग यह इंगित करने के लिए किया जाता है कि जिस दृश्य को लपेटा जाना चाहिए वह संपूर्ण ऐप लेता है या केवल एक छोटा सा अनुभाग। इसका उपयोग किनारे पर कुछ सजावट लगाने से बचने के लिए किया जा सकता है जो केवल तभी समझ में आती हैं जब वे पूरी स्क्रीन के किनारे पर दिखाई देती हैं। एक नमूना ऐप जो गैर-फ़ुलस्क्रीन बेस लेआउट का उपयोग करता है वह सेटिंग्स है, जिसमें दोहरे फलक लेआउट के प्रत्येक फलक का अपना टूलबार होता है।
चूंकि toolbarEnabled
false
होने पर installBaseLayoutAround
के लिए null वापस लौटने की उम्मीद की जाती है, इसलिए प्लगइन को यह इंगित करने के लिए कि वह बेस लेआउट को कस्टमाइज़ नहीं करना चाहता है, उसे customizesBaseLayout
से false
वापस आना होगा।
रोटरी नियंत्रणों को पूरी तरह से समर्थन देने के लिए बेस लेआउट में एक FocusParkingView
और एक FocusArea
होना चाहिए। इन दृश्यों को उन उपकरणों पर छोड़ा जा सकता है जो रोटरी का समर्थन नहीं करते हैं। FocusParkingView/FocusAreas
स्थिर कारयूआई लाइब्रेरी में कार्यान्वित किया जाता है, इसलिए संदर्भों से दृश्य बनाने के लिए फ़ैक्टरियां प्रदान करने के लिए setRotaryFactories
उपयोग किया जाता है।
फोकस दृश्य बनाने के लिए उपयोग किए जाने वाले संदर्भ स्रोत संदर्भ होने चाहिए, न कि प्लगइन का संदर्भ। FocusParkingView
को यथासंभव पेड़ में पहले दृश्य के सबसे करीब होना चाहिए, क्योंकि यह वह है जिस पर ध्यान केंद्रित किया जाता है जब उपयोगकर्ता को कोई फोकस दिखाई नहीं देना चाहिए। FocusArea
यह इंगित करने के लिए टूलबार को बेस लेआउट में लपेटना होगा कि यह एक रोटरी नज ज़ोन है। यदि FocusArea
प्रदान नहीं किया गया है, तो उपयोगकर्ता रोटरी नियंत्रक के साथ टूलबार में किसी भी बटन पर नेविगेट करने में असमर्थ है।
टूलबार नियंत्रक
मूल लेआउट की तुलना में वास्तविक ToolbarController
को लागू करना अधिक सरल होना चाहिए। इसका काम अपने सेटर्स को दी गई जानकारी लेना और उसे बेस लेआउट में प्रदर्शित करना है। अधिकांश विधियों की जानकारी के लिए Javadoc देखें। कुछ अधिक जटिल तरीकों पर नीचे चर्चा की गई है।
getImeSearchInterface
का उपयोग IME (कीबोर्ड) विंडो में खोज परिणाम दिखाने के लिए किया जाता है। यह कीबोर्ड के साथ-साथ खोज परिणामों को प्रदर्शित/एनिमेट करने के लिए उपयोगी हो सकता है, उदाहरण के लिए यदि कीबोर्ड केवल स्क्रीन का आधा हिस्सा लेता है। अधिकांश कार्यक्षमता स्थिर CarUi लाइब्रेरी में कार्यान्वित की जाती है, प्लगइन में खोज इंटरफ़ेस केवल स्थिर लाइब्रेरी के लिए TextView
और onPrivateIMECommand
कॉलबैक प्राप्त करने के तरीके प्रदान करता है। इसका समर्थन करने के लिए, प्लगइन को एक TextView
उपवर्ग का उपयोग करना चाहिए जो onPrivateIMECommand
ओवरराइड करता है और दिए गए श्रोता को कॉल को उसके खोज बार के TextView
के रूप में पास करता है।
setMenuItems
केवल स्क्रीन पर MenuItems प्रदर्शित करता है, लेकिन इसे आश्चर्यजनक रूप से अक्सर कहा जाएगा। चूंकि MenuItems के लिए प्लगइन एपीआई अपरिवर्तनीय है, जब भी कोई MenuItem बदला जाता है, तो एक बिल्कुल नया setMenuItems
कॉल होगा। यह इतनी छोटी सी बात के लिए हो सकता है जैसे कि किसी उपयोगकर्ता ने स्विच मेनूआइटम पर क्लिक किया और उस क्लिक के कारण स्विच चालू हो गया। प्रदर्शन और एनीमेशन दोनों कारणों से, इसलिए पुरानी और नई मेनूआइटम सूची के बीच अंतर की गणना करने और केवल उन दृश्यों को अपडेट करने के लिए प्रोत्साहित किया जाता है जो वास्तव में बदल गए हैं। MenuItems एक key
फ़ील्ड प्रदान करता है जो इसमें मदद कर सकता है, क्योंकि कुंजी एक ही MenuItem के लिए setMenuItems
पर विभिन्न कॉलों में समान होनी चाहिए।
AppStyledView
AppStyledView
एक ऐसे दृश्य के लिए एक कंटेनर है जो बिल्कुल भी अनुकूलित नहीं है। इसका उपयोग उस दृश्य के चारों ओर एक बॉर्डर प्रदान करने के लिए किया जा सकता है जो इसे बाकी ऐप से अलग बनाता है, और उपयोगकर्ता को संकेत देता है कि यह एक अलग प्रकार का इंटरफ़ेस है। AppStyledView द्वारा लपेटा गया दृश्य setContent
में दिया गया है। ऐप के अनुरोध के अनुसार AppStyledView
में एक बैक या क्लोज़ बटन भी हो सकता है।
AppStyledView
तुरंत अपने दृश्यों को installBaseLayoutAround
की तरह दृश्य पदानुक्रम में सम्मिलित नहीं करता है, इसके बजाय यह केवल getView
माध्यम से अपने दृश्य को स्थिर लाइब्रेरी में लौटाता है, जो फिर सम्मिलन करता है। AppStyledView
की स्थिति और आकार को getDialogWindowLayoutParam
लागू करके भी नियंत्रित किया जा सकता है।
संदर्भों
संदर्भों का उपयोग करते समय प्लगइन को सावधान रहना चाहिए, क्योंकि प्लगइन और "स्रोत" दोनों संदर्भ हैं। प्लगइन संदर्भ getPluginFactory
के तर्क के रूप में दिया गया है, और यह एकमात्र संदर्भ है जिसमें प्लगइन के संसाधन हैं। इसका मतलब यह है कि यह एकमात्र संदर्भ है जिसका उपयोग प्लगइन में लेआउट को बढ़ाने के लिए किया जा सकता है।
हालाँकि, प्लगइन संदर्भ में सही कॉन्फ़िगरेशन सेट नहीं हो सकता है। सही कॉन्फ़िगरेशन प्राप्त करने के लिए, हम घटकों को बनाने वाली विधियों में स्रोत संदर्भ प्रदान करते हैं। स्रोत संदर्भ आमतौर पर एक गतिविधि है, लेकिन कुछ मामलों में एक सेवा या अन्य एंड्रॉइड घटक भी हो सकता है। प्लगइन संदर्भ से संसाधनों के साथ स्रोत संदर्भ से कॉन्फ़िगरेशन का उपयोग करने के लिए, createConfigurationContext
का उपयोग करके एक नया संदर्भ बनाया जाना चाहिए। यदि सही कॉन्फ़िगरेशन का उपयोग नहीं किया जाता है, तो एंड्रॉइड सख्त मोड का उल्लंघन होगा, और फुलाए गए दृश्यों में सही आयाम नहीं हो सकते हैं।
Context layoutInflationContext = pluginContext.createConfigurationContext(
sourceContext.getResources().getConfiguration());
मोड बदलता है
कुछ प्लगइन्स अपने घटकों के लिए कई मोड का समर्थन कर सकते हैं, जैसे स्पोर्ट मोड या इको मोड जो देखने में अलग दिखते हैं। CarUi में ऐसी कार्यक्षमता के लिए कोई अंतर्निहित समर्थन नहीं है, लेकिन प्लगइन को इसे पूरी तरह से आंतरिक रूप से लागू करने से कोई नहीं रोक सकता है। प्लगइन किसी भी स्थिति की निगरानी कर सकता है, यह पता लगाना चाहता है कि मोड कब स्विच करना है, जैसे प्रसारण सुनना। प्लगइन मोड बदलने के लिए कॉन्फ़िगरेशन परिवर्तन को ट्रिगर नहीं कर सकता है, लेकिन वैसे भी कॉन्फ़िगरेशन परिवर्तनों पर भरोसा करने की अनुशंसा नहीं की जाती है, क्योंकि प्रत्येक घटक की उपस्थिति को मैन्युअल रूप से अपडेट करना उपयोगकर्ता के लिए आसान है और उन बदलावों की भी अनुमति देता है जो कॉन्फ़िगरेशन परिवर्तनों के साथ संभव नहीं हैं।
जेटपैक कम्पोज़
प्लगइन्स को जेटपैक कंपोज़ का उपयोग करके कार्यान्वित किया जा सकता है, लेकिन यह एक अल्फा-स्तरीय सुविधा है और इसे स्थिर नहीं माना जाना चाहिए।
रेंडर करने के लिए कंपोज़-सक्षम सतह बनाने के लिए प्लगइन्स ComposeView
उपयोग कर सकते हैं। यह ComposeView
वह होगा जो घटकों में getView
विधि से ऐप पर लौटाया जाता है।
ComposeView
का उपयोग करने में एक प्रमुख मुद्दा यह है कि यह वैश्विक चर को संग्रहीत करने के लिए लेआउट में रूट व्यू पर टैग सेट करता है जो पदानुक्रम में विभिन्न ComposeViews में साझा किए जाते हैं। चूंकि प्लगइन की संसाधन आईडी को ऐप से अलग से नामित नहीं किया गया है, इसलिए जब ऐप और प्लगइन दोनों एक ही दृश्य पर टैग सेट करते हैं तो इससे टकराव हो सकता है। एक कस्टम ComposeViewWithLifecycle
जो इन वैश्विक चरों को ComposeView
में ले जाता है, नीचे दिया गया है। फिर, इसे स्थिर नहीं माना जाना चाहिए।
ComposeViewWithLifecycle
:
class ComposeViewWithLifecycle @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr),
LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner {
private val lifeCycle = LifecycleRegistry(this)
private val modelStore = ViewModelStore()
private val savedStateRegistryController = SavedStateRegistryController.create(this)
private var composeView: ComposeView? = null
private var content = @Composable {}
init {
ViewTreeLifecycleOwner.set(this, this)
ViewTreeViewModelStoreOwner.set(this, this)
ViewTreeSavedStateRegistryOwner.set(this, this)
compositionContext = createCompositionContext()
}
fun setContent(content: @Composable () -> Unit) {
this.content = content
composeView?.setContent(content)
}
override fun getLifecycle(): Lifecycle {
return lifeCycle
}
override fun getViewModelStore(): ViewModelStore {
return modelStore
}
override fun getSavedStateRegistry(): SavedStateRegistry {
return savedStateRegistryController.savedStateRegistry
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
savedStateRegistryController.performRestore(Bundle())
lifeCycle.currentState = Lifecycle.State.RESUMED
composeView = ComposeView(context)
composeView?.setContent(content)
addView(composeView, LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
lifeCycle.currentState = Lifecycle.State.DESTROYED
modelStore.clear()
removeAllViews()
composeView = null
}
// Exact copy of View.createCompositionContext() in androidx's WindowRecomposer.android.kt
private fun createCompositionContext(): CompositionContext {
val currentThreadContext = AndroidUiDispatcher.CurrentThread
val pausableClock = currentThreadContext[MonotonicFrameClock]?.let {
PausableMonotonicFrameClock(it).apply { pause() }
}
val contextWithClock = currentThreadContext + (pausableClock ?: EmptyCoroutineContext)
val recomposer = Recomposer(contextWithClock)
val runRecomposeScope = CoroutineScope(contextWithClock)
val viewTreeLifecycleOwner = checkNotNull(ViewTreeLifecycleOwner.get(this)) {
"ViewTreeLifecycleOwner not found from $this"
}
viewTreeLifecycleOwner.lifecycle.addObserver(
LifecycleEventObserver { _, event ->
@Suppress("NON_EXHAUSTIVE_WHEN")
when (event) {
Lifecycle.Event.ON_CREATE ->
// Undispatched launch since we've configured this scope
// to be on the UI thread
runRecomposeScope.launch(start = CoroutineStart.UNDISPATCHED) {
recomposer.runRecomposeAndApplyChanges()
}
Lifecycle.Event.ON_START -> pausableClock?.resume()
Lifecycle.Event.ON_STOP -> pausableClock?.pause()
Lifecycle.Event.ON_DESTROY -> {
recomposer.cancel()
}
}
}
)
return recomposer
}
// TODO: ComposeViewWithLifecycle should handle saving state and other lifecycle things
// override fun onSaveInstanceState(): Parcelable? {
// val superState = super.onSaveInstanceState()
// val bundle = Bundle()
// savedStateRegistryController.performSave(bundle)
// }
}