主頁 > 移動端開發 > AppCompat發布兩年了,還沒了解?

AppCompat發布兩年了,還沒了解?

2021-01-17 14:00:36 移動端開發

近日隨筆
近期疫情日漸嚴峻,大家多多保重,出門記得戴口罩,希望河北,黑龍江能盡早控制住好局面迎來拐點,全國人民過個好年,

在這里插入圖片描述

為了能夠讓低版本的Android系統能夠運行新特性,AppCompat框架自Support時代就已推出,但隨著AndroidX的一統江湖,AppCompat的相關類則一并遷移到了AndroidX庫里,

Android開發者應該都不陌生,在Android Studio上創建的專案默認采用AppCompatActivity作為Activity的基類,可以說,這個類是整個AppCompat框架里最重要的類,也是我們今天研究AppCompat的起點,

AppCompatActivity

其間接繼承自Activity,之間還繼承了其他Activity特色類,可以使得低版本上運行的Activity也能擁有ToolBar和暗黑主題等新功能,

AppCompatActivity extends FragmentActivity extends ComponentActivity extends ComponentActivity extends Activity*

  • FragmentActivity
    采用FragmentController類對AndroidX的Fragment新組件提供支撐,比如提供了咱們常用的getSupportFragmentManager() API,

  • androidx.activity.ComponentActivity
    實作了ViewModel介面,和Lifecycle框架進行配合以支撐ViewModel框架的運行,

  • androidx.core.app.ComponentActivity
    實作了Lifecycle介面并通過ReportFragment支撐Lifecycle框架的運行,

先來感受一下AppCompatActivity和Activity在UI上的表現,

在這里插入圖片描述

從對比圖上看并沒有太大區別,但從UI的樹形圖上看是有些區別的,比如AppCompatActivity的content區域的上方多了一個LinearLayout和ViewStub控制元件,再比如AppCompatActivity下面的是AppCompatTextView而不是TextView,

那這些差異是如何實作的,有什么用意?

談到AppCompatActivity實作的話不得不提幕后的大管家AppCompatDelegate類,其承載了AppCompatActivity幾乎所有的實作作業,比如AppCompatActivity復寫了setContentView()的邏輯,交由大管家AppCompatDelegate去實作其特有的UI結構,

AppCompatDelegate

重點介紹下大管家的頭號作業setContentView(),具體分為如下幾個小任務,

  1. ensureSubDecor() 確保ActionBar的特有UI結構創建完畢

  2. removeAllViews() 確保ContentView的所有Child全部被移除干凈

  3. inflate() 將畫面的內容布局決議并添加到ContentView

第一步ensureSubDecor()的內容比較多,又分為幾個子任務,包括呼叫createSubDecor()創建ActionBar特有布局,setWindowTitle()將Activity標題反映到ToolBar上以及applyFixedSizeWindow()去調整DecorView尺寸,

核心內容在于createSubDecor()這個子任務,它需要確保ActionBar的特有布局創建出來并和Window的DecorView產生聯系,

  1. ensureWindow()
    獲取Activity所屬的Window參考并添加window相關回呼

  2. getDecorView()
    告知Window去創建DecorView,這里要提一下PhoneWindow的generateLayout(),其將依據主題的創建不同的布局結構,比如AppCompatActivity的話將決議screen_simple.xml得到DecorView的基本結構,其包括根布局LinearLayout,用來映射actionmode布局的viewstub以及承載App內容的id為ContentView

  3. inflate()
    獲取ActionBar的布局,主要是abc_screen_toolbar.xml和abc_screen_content_include.xml兩個檔案

  4. removeViewAt()和addView()
    ContentView的子View遷移至ActionBar布局下,具體方法是將其所有child移除并add到ActionBar布局下id為action_bar_activity_content的ViewGroup下面,并將原有ContentView的id置空,同時將該目標ViewGroup的id設定為Content,意味著它將成為AppCompatActivity畫面承載內容區域的父布局

公開的API

除了setContentView()在打造布局結構上的差異,AppCompatActivity還提供了些Activity所沒有的API供開發者使用,

  • getSupportActionBar() 用以獲取AppCompat特有的ActionBar組件供開發者定制ActionBar

  • getDelegate() 獲取AppCompatActivity內部實作的大管家AppCompatDelegate的實體(實際上將通過靜態的create()獲取實作類AppCompatDelegateImpl的實體)

  • getDrawerToggleDelegate() 獲取抽屜導航布局DrawerLayout的代理類ActionBarDrawableToggleImpl的實體,用來和ActionBar進行UI的互動

  • onNightModeChanged() 不同于配置了uiMode的外部配置變更后才能收到主題變化的通知,本API可以在暗黑主題的適配模式(比如跟隨系統設定模式和跟隨電量設定模式等)發生變化后得到回呼,可利用這個時機做些補充處理

使用上的注意

AppCompatActivity的注釋上有如下說明,推薦采用Theme.AppCompat主題,

You can add an ActionBar to your activity when running on API level 7 or higher by extending this class for your activity and setting the activity theme to Theme.AppCompat or a similar theme.

經過驗證如果我們使用了別的主題就會得到如下的crash,

You need to use a Theme.AppCompat theme (or descendant) with this activity.

原理在于上面自己的大管家AppCompatDelegate在創建ActionBar布局的時候有意地確保Activity是否采用了AppCompatTheme主題,尤其是如果沒有指定AppCompat定義的windowActionBar的屬性的話,將拋出如上的例外,

// AppCompatThemeImpl.java
	private ViewGroup createSubDecor() {
        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);

        if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
            a.recycle();
            throw new IllegalStateException(
                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
        }
        ...
	}

至于為什么用例外來確保AppCompatTheme的采用,因為后續的處理跟AppCompatTheme息息相關,如果沒有采用后面的很多處理將失效,

AppCompatDialog

除了使用極高的AppCompatActivity以外,AppCompatDialog的曝光率也不低,其實作原理和AppCompatActivity企劃一致,都是依賴大管家AppCompatDelegate進行實作,一樣是為了在Dialog的基礎上擴展出新ToolBar和暗黑主題的支持,

AppCompatTheme

前面提到的AppCompatTheme主要分為兩個主題,

  • Theme.AppCompat
    繼承自Base.V7.Theme.AppCompat主題,指定AppCompatViewInflater為widget等class的決議類,并設定AppCompatTheme所定義的基本屬性,其頂級主題仍舊是老牌的主題Theme.Holo

  • Theme.AppCompat.DayNight
    能夠自動適配暗黑主題,其繼承自Base.V7.Theme.AppCompat.Light,與Theme.AppCompat的區別主要在于其默認情況下采用了light系的主題,比如colorPrimary采用primary_material_light,而Theme.AppCompat則采用primary_material_dark顏色

App采用了該主題就可以自動適配暗黑模式,這是如何做到的?

Dark Theme 暗黑模式

AppCompatActivity在系結BaseContext的時候會通過AppCompatDelegate的applyDayNight()去決議App設定的暗黑主題模式并做出一些相應的配置作業,

比如常用的跟隨省電模式,其指的是設備的省電模式開啟后將自動進入暗黑主題,降低功耗,反之關閉之后回傳到白天主題,

具體實作是AppCompatDelegate將注冊監聽省電模式變化的廣播(ACTION_POWER_SAVE_MODE_CHANGED),當省電模式開啟/關閉時,廣播接收器將自動回呼updateForNightMode()去更新對應的主題,

    private boolean applyDayNight(final boolean allowRecreation) {
        ...
        @NightMode final int nightMode = calculateNightMode();
        @ApplyableNightMode final int modeToApply = mapNightMode(nightMode);
        final boolean applied = updateForNightMode(modeToApply, allowRecreation);
        ...
        if (nightMode == MODE_NIGHT_AUTO_BATTERY) {
            // 注冊監聽省電模式的廣播接收器
            getAutoBatteryNightModeManager().setup();
        }
		...
    }

    abstract class AutoNightModeManager {
        ...
        void setup() {
            ...
            if (mReceiver == null) {
                mReceiver = new BroadcastReceiver() {
                    @Override
                    public void onReceive(Context context, Intent intent) {
                        // 省電模式變化后的回呼
                        onChange();
                    }
                };
            }
            mContext.registerReceiver(mReceiver, filter);
        }
        ...
    }

    private class AutoBatteryNightModeManager extends AutoNightModeManager {
        ...
        @Override
        public void onChange() {
            // 省電模式變化后回呼主題切換方法更新主題
            applyDayNight();
        }

        @Override
        IntentFilter createIntentFilterForBroadcastReceiver() {
            if (Build.VERSION.SDK_INT >= 21) {
                IntentFilter filter = new IntentFilter();
                filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
                return filter;
            }
            return null;
        }
    }

更新主題的處理則是如下關鍵代碼,

    private boolean updateForNightMode(final int mode, final boolean allowRecreation) {
        ...
        // 如果Activity的BaseContext尚未初始化則直接適配新的主題值
        if ((sAlwaysOverrideConfiguration || newNightMode != applicationNightMode)
                && !mBaseContextAttached
				...) {
            ...
            try {
                ...
                ((android.view.ContextThemeWrapper) mHost).applyOverrideConfiguration(conf);
                handled = true;
            ...
            }
        }

        final int currentNightMode = mContext.getResources().getConfiguration().uiMode
                & Configuration.UI_MODE_NIGHT_MASK;

        // 如果Activity的BaseContext已經創建,
        // 且App沒有宣告要處理暗黑主題變化的話,將重繪Activity
        if (!handled
                ...) {
            ActivityCompat.recreate((Activity) mHost);
            handled = true;
        }

        // 假使App宣告了處理暗黑主題變化的話,
        // 那么將新的主題值更新到Configuration的uiMode屬性
        // 并回呼Activity#onConfigurationChanged(),等待App的自行處理
        if (!handled && currentNightMode != newNightMode) {
            ...
            updateResourcesConfigurationForNightMode(newNightMode, activityHandlingUiMode);
            handled = true;
        }

        // 最后檢查是否要通知App暗黑主題模式發生變化
        // (注意這里指的是App設定的暗黑主題切換的策略發生變更,
        // 比如由跟隨系統設定變更為固定暗黑模式等)
        if (handled && mHost instanceof AppCompatActivity) {
            ((AppCompatActivity) mHost).onNightModeChanged(mode);
        }
        ...
    }

細心的開發者可能會注意到我們平常在AppCompatActivity的布局里使用的控制元件,最終得到的類名稱里會多上AppCompat的前綴,比如宣告的是TextView控制元件最后得到的是AppCompatTextView類的實體,這是怎么做到的,為什么這么做?這就離不開AppCompatViewInflater的默默付出,

AppCompatViewInflater

核心功能就是將布局里的控制元件切換為AppCompat版本,在呼叫LayoutInflater決議App布局的階段,大管家AppCompatDelegate將呼叫AppCompatViewInflater將布局中的控制元件逐個替換,

    final View createView(View parent, final String name, @NonNull Context context...) {
        ...
        switch (name) {
            case "TextView":
                view = createTextView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "ImageView":
                view = createImageView(context, attrs);
                verifyNotNull(view, name);
                break;
            ...
        }
        ...
        return view;
    }
	
	protected AppCompatTextView createTextView(Context context, AttributeSet attrs) {
        return new AppCompatTextView(context, attrs);
    }

除了上面提到的AppCompatTextView,AppCompat的widget目錄下有很多為了兼容新特性擴展的控制元件,以AppCompatTextView和另一個常用的AppCompatImageView來一探究竟,

AppCompatTextView

由代碼注釋就可以看出來該控制元件在TextView的基礎上增加了Dynamic TintAuto Size兩大特性,

先看下這兩特性大體是什么效果,

在這里插入圖片描述

可以看到第二個TextView對背景著上了更深的綠色,并對icon著上了白色,使得它內部的icon和文字相較第一個TextView看起來更清楚,這是通過AppCompatTextView提供的backgroundTint和drawableTint屬性實作的,這種給背景和icon動態著色的功能就是Dynamic Tint特性,

另外可以看到最下面TextView的文本內容正好鋪滿整個螢屏沒有在末尾出現省略,而上面那個TextView的字體尺寸較大且在尾部用省略號表示,這種自動適配字體尺寸的效果同樣是依賴AppCompatTextView提供的相關屬性來完成,此為Auto Size特性,

Dynamic Tint

主要依賴AppCompatBackgroundHelper和AppCompatDrawableManager實作,包括反映靜態配置和動態修改的Tint屬性,

主要經歷這幾步:

  1. loadFromAttributes() 決議布局里配置的Tint屬性,核心處理在于能夠將設定的Tint資源決議成ColorStateList實體,
// ColorStateListInflaterCompat.java
    private static ColorStateList inflate(Resources r, XmlPullParser parser) {
        ...
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
            ...
            final int color = modulateColorAlpha(baseColor, alphaMod);
            colorList = GrowingArrayUtils.append(colorList, listSize, color);
            stateSpecList = GrowingArrayUtils.append(stateSpecList, listSize, stateSpec);
            listSize++;
        }
        ...
        return new ColorStateList(stateSpecs, colors);
    }
  1. setInternalBackgroundTint()和applySupportBackgroundTint() 負責管理和區分Tint顏色的取自靜態配置的屬性還是外部動態配置的引數

  2. tintDrawable()負責著色,本質在于呼叫Drawable#setColorFilter()去重繪顏色的繪制

// ResourceManagerInternal.java
    static void tintDrawable(Drawable drawable, TintInfo tint, int[] state) {
        ...
        if (tint.mHasTintList || tint.mHasTintMode) {
            drawable.setColorFilter(createTintFilter(
                    tint.mHasTintList ? tint.mTintList : null,
                    tint.mHasTintMode ? tint.mTintMode : DEFAULT_MODE,
                    state));
        } else {
            drawable.clearColorFilter();
        }
        ...
    }

Auto Size

需要解決的問題是對Text內容依據最大寬度和當前size計算自適應的最佳字體尺寸,依賴AppCompatTextHelper和AppCompatTextViewAutoSizeHelper實作,

  1. 決議AutoSize相關屬性的配置并設定是否需要自動適配字體尺寸的Flag,
// AppCompatTextViewAutoSizeHelper.java
	void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
        ...
        if (a.hasValue(R.styleable.AppCompatTextView_autoSizeTextType)) {
            mAutoSizeTextType = a.getInt(R.styleable.AppCompatTextView_autoSizeTextType,
                    TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
        }
        ...
        if (supportsAutoSizeText()) {
            if (mAutoSizeTextType == TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM) {
                ...
                setupAutoSizeText();
            }
        ...
        }
    }

    private boolean setupAutoSizeText() {
        if (supportsAutoSizeText()
                && mAutoSizeTextType == TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM) {
            ...
            if (!mHasPresetAutoSizeValues || mAutoSizeTextSizesInPx.length == 0) {
                ...
                for (int i = 0; i < autoSizeValuesLength; i++) {
                    autoSizeTextSizesInPx[i] = Math.round(
                            mAutoSizeMinTextSizeInPx + (i * mAutoSizeStepGranularityInPx));
                }
                mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(autoSizeTextSizesInPx);
            }
            mNeedsAutoSizeText = true;
        }
		...
    }
  1. 在文本內容初始化或變化的時候計算合適的字體尺寸并反映到UI上,
// AppCompatTextView.java
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        ...
        if (mTextHelper != null && !PLATFORM_SUPPORTS_AUTOSIZE && mTextHelper.isAutoSizeEnabled()) {
            mTextHelper.autoSizeText();
        }
    }

// AppCompatTextHelper.java
    void autoSizeText() {
        mAutoSizeTextHelper.autoSizeText();
    }

// AppCompatTextViewAutoSizeHelper.java
	void autoSizeText() {
        ...
        if (mNeedsAutoSizeText) {
            ...
            synchronized (TEMP_RECTF) {
                ...
                // 計算最佳size
                final float optimalTextSize = findLargestTextSizeWhichFits(TEMP_RECTF);
                // 如果和預設的size不一致的話更新size
                if (optimalTextSize != mTextView.getTextSize()) {
                    setTextSizeInternal(TypedValue.COMPLEX_UNIT_PX, optimalTextSize);
                }
            }
        }
        ...
    }

AppCompatImageView

AppCompatTextView一樣擴展了針對background和src的Dynamic Tint功能,

在這里插入圖片描述

AppCompatTextView不同的是AppCompatImageView對icon著色采用的屬性不是*attr#drawableTintattr#tint***,由AppCompatImageHelperImageViewCompat類實作,原理大同小異,不再贅述,

輔助類

AppCompat框架的開發人員在實作AppCompat擴展控制元件等特性的時候用到很多輔助類,大家可以自行研究下其細節,學習下一些巧妙的實作思路,

  • AppCompatBackgroundHelper
  • AppCompatDrawableManager
  • AppCompatTextHelper
  • AppCompatTextViewAutoSizeHelper
  • AppCompatTextClassifierHelper
  • AppCompatResources
  • AppCompatImageHelper

類圖

最后上一下AppCompat框架的簡易類圖,幫助大家有個整體上的認識,

在這里插入圖片描述

總結

可以看到AppCompat框架整體比較簡單,因此也容易被大家忽視,但作為Jetpack系列里的入口,了解一下很有必要,

原創不易,如果本篇文章引起你的思考和興趣,歡迎點贊,收藏和關注,如果想要了解更多更全資訊,掃碼關注博主公眾號TechMerger
在這里插入圖片描述

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/249933.html

標籤:其他

上一篇:安卓RecycleView實作九宮格和點擊事件(詳解)

下一篇:android學習----注冊頁面實作跳轉

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more