android 9.0 Launcher3 去掉抽屜式,顯示所有 app

来源:https://www.cnblogs.com/cczheng-666/archive/2019/11/08/11818825.html
-Advertisement-
Play Games

效果圖 修改思路 1、增加全局控制變數 sys.launcher3.is_full_app ,用來動態切換 2、增加兩套佈局,對應有抽屜和無抽屜 3、去除 allAppsButton 4、將 AllAppsContainerView 中的圖標載入到 Workspace 5、新安裝的 app 自動添加 ...


效果圖

在這裡插入圖片描述

修改思路

1、增加全局控制變數 sys.launcher3.is_full_app,用來動態切換

2、增加兩套佈局,對應有抽屜和無抽屜

3、去除 allAppsButton

4、將 AllAppsContainerView 中的圖標載入到 Workspace

5、新安裝的 app 自動添加圖標到 Workspace

6、替換 Workspace 圖標長按刪除選項為取消

7、屏蔽上拉顯示抽屜頁面手勢

8、修改頁面指示線為圓點

上代碼

1、增加全局控制變數 sys.launcher3.is_full_app

1) 在 LauncherAppState 中增加靜態方法 isDisableAllApps(), 通過修改 Settings 中自定義的值 sys.launcher3.is_full_app

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\LauncherAppState.java

   private static  Context mContext;

public static boolean isDisableAllApps() {
        if (mContext != null) {
            return Settings.System.getInt(mContext.getContentResolver(), 
                "sys.launcher3.is_full_app", 0) == 1;
        }
        return true;
    }

vendor\mediatek\proprietary\packages\apps\Launcher3\AndroidManifest-common.xml

2) AndroidManifest-common.xml 中增加許可權

<uses-permission android:name="android.permission.WRITE_SETTINGS" />

3) 在 SettingsActivity 中增加 SwitchPreference 用以動態修改 sys.launcher3.is_full_app

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\SettingsActivity.java

在內部類 LauncherSettingsFragment 中重寫 onPreferenceTreeClick() 用以監聽 SwitchPreference 點擊

/**
     * This fragment shows the launcher preferences.
     */
    public static class LauncherSettingsFragment extends PreferenceFragment {
    
    .....
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

    ....

            //讀取保存的值,初始化 SwitchPreference 的初始狀態,是否選中
            int isFull = Settings.System.getInt(getActivity().getContentResolver(), 
                "sys.launcher3.is_full_app", 0);
            Log.d("Launcher3", "sys.launcher3.is_full_app="+isFull);
            SwitchPreference fullSwitch = (SwitchPreference) findPreference("pref_is_full_app");
            fullSwitch.setChecked(isFull==1);
     }


    
    //add for change is_full_app value
    @Override
    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
            boolean result = true;
            final String key = preference.getKey();

             if ("pref_is_full_app".equals(key)) {
                boolean checked = ((SwitchPreference) preference).isChecked();

                Settings.System.putInt(getActivity().getContentResolver(), "sys.launcher3.is_full_app",
                        checked ? 1 : 0);

                Log.e("Launcher3", "SwitchPreference checked="+checked);

                // Value has changed
                ProgressDialog.show(getActivity(),
                        null /* title */,
                        getActivity().getString(R.string.full_app_override_progress),
                        true /* indeterminate */,
                        false /* cancelable */);
                new LooperExecutor(LauncherModel.getWorkerLooper()).execute(
                        new OverrideApplyHandler(getActivity()));
                
            }
            return result;
        }

}

點擊 SwitchPreference 後需要保存 sys.launcher3.is_full_app 新值,同時清除 Launcher3 的緩存,延時啟動並結束當前進程

清除緩存方法 clearApplicationUserData 在 Launcher3 中編譯報錯,所以通過發送廣播到 Setting 中進行真正的清緩存操作


    //add for change is_full_app value
    private static class OverrideApplyHandler implements Runnable {

        private final Context mContext;

        private OverrideApplyHandler(Context context) {
            mContext = context;
        }

        @Override
        public void run() {
            // Clear the icon cache.
            LauncherAppState.getInstance(mContext).getIconCache().clear();

            // Wait for it
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                Log.e("Launcher3", "Error waiting", e);
            }

            // Schedule an alarm before we kill ourself.
            Intent homeIntent = new Intent(Intent.ACTION_MAIN)
                    .addCategory(Intent.CATEGORY_HOME)
                    .setPackage(mContext.getPackageName())
                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            PendingIntent pi = PendingIntent.getActivity(mContext, 42,
                    homeIntent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
            mContext.getSystemService(AlarmManager.class).setExact(
                    AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 50, pi);

            //clear data will kill process
            Intent intent = new Intent("com.android.action.CLEAR_APP_DATA");
            intent.putExtra("pkgName", "com.android.launcher3");
            intent.addFlags(0x01000000);
            mContext.sendBroadcast(intent);
            Log.i("Launcher3", "Clearing user data com.android.launcher3");

            // Kill process
            android.os.Process.killProcess(android.os.Process.myPid());
        }
    }

4) SettingsActivity 對應的 xml 文件修改 launcher_preferences

vendor\mediatek\proprietary\packages\apps\Launcher3\res\xml\launcher_preferences.xml

 <SwitchPreference
        android:key="pref_is_full_app"
        android:title="@string/is_full_app_title"
        android:summary="@string/is_full_app_desc"
        android:defaultValue="false"
        android:persistent="true" />

對應的 string 文件就不貼了,自己增加下就行

2、增加兩套佈局,對應有抽屜和無抽屜

載入佈局文件對應的 xml 為 vendor\mediatek\proprietary\packages\apps\Launcher3\res\xml\device_profiles.xml

Launcher3 通過獲取 minWidthDps 和 minHeightDps 來確定載入哪一個 profile,我的平板解析度是 1280*800 的,增加兩個 profile 節點

 <profile
        launcher:name="Tablet"
        launcher:minWidthDps="376"
        launcher:minHeightDps="586"
        launcher:numRows="4"
        launcher:numColumns="5"
        launcher:numFolderRows="4"
        launcher:numFolderColumns="5"
        launcher:iconSize="50"
        launcher:iconTextSize="11"
        launcher:numHotseatIcons="5"
        launcher:defaultLayoutId="@xml/default_workspace_tb_5x6"
        />

    <profile
        launcher:name="Tablet_no_all_app"
        launcher:minWidthDps="380"
        launcher:minHeightDps="590"
        launcher:numRows="4"
        launcher:numColumns="5"
        launcher:numFolderRows="4"
        launcher:numFolderColumns="5"
        launcher:iconSize="50"
        launcher:iconTextSize="11"
        launcher:numHotseatIcons="4"
        launcher:defaultLayoutId="@xml/default_workspace_tb_5x6_no_all_app"
        />

對應的你需要在 xml 文件下增加 4 個文件,分別是 default_workspace_tb_5x6.xml dw_hotseat_tb.xml default_workspace_tb_5x6_no_all_app.xml dw_hotseat_tb_no_all_app.xml

這樣的好處是你可以自定義不同的佈局文件載入內容,上面的配置含義簡單說一下,分別是最小寬度、最小高度、佈局的行和列、文件夾中佈局行和列、圖標大小、圖標文字大小、HotSeat 個數,載入的佈局文件

在 InvariantDeviceProfile() 判斷是否需要載入 Tablet_no_all_app profile

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\InvariantDeviceProfile.java

public InvariantDeviceProfile(Context context) {

        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        DisplayMetrics dm = new DisplayMetrics();
        display.getMetrics(dm);

        Point smallestSize = new Point();
        Point largestSize = new Point();
        display.getCurrentSizeRange(smallestSize, largestSize);

        // This guarantees that width < height
        minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm);
        minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm);
        Log.i("Launcher3.profiles", "orignalminWidthDps="+minWidthDps + "  orignalminHeightDps="+minHeightDps);
        
        //add for load no_all_app xml
        if (LauncherAppState.isDisableAllApps()) {
            Log.e("Launcher3.profiles", "load no all app profiles");
            //對應 device_profiles.xml 中 Tablet_no_all_app 的值
            minWidthDps = 380.0f;
            minHeightDps = 590.0f;
        }
        .....
}

3、去除 allAppsButton

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\Hotseat.java

將 resetLayout() 中 FeatureFlags.NO_ALL_APPS_ICON 替換為 LauncherAppState.isDisableAllApps()

void resetLayout(boolean hasVerticalHotseat) {
        mContent.removeAllViewsInLayout();
        mHasVerticalHotseat = hasVerticalHotseat;
        InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
        if (hasVerticalHotseat) {
            mContent.setGridSize(1, idp.numHotseatIcons);
        } else {
            mContent.setGridSize(idp.numHotseatIcons, 1);
        }

        //if (!FeatureFlags.NO_ALL_APPS_ICON) {
        /// add for check is need allappbutton
        if (!LauncherAppState.isDisableAllApps()) {
            // Add the Apps button
            Context context = getContext();
            DeviceProfile grid = mLauncher.getDeviceProfile();

        ...
}

4、將 AllAppsContainerView 中的圖標載入到 Workspace

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\model\LoaderTask.java

run() 中增加判斷,添加 verifyApplications(), 修改 InstallShortcutReceiver 中 PendingInstallShortcutInfo 為 public

public void run() {
        synchronized (this) {
            // Skip fast if we are already stopped.
            if (mStopped) {
                return;
            }
        }
        ....

        // second step
        TraceHelper.partitionSection(TAG, "step 2.1: loading all apps");
        loadAllApps();

        //add for load all app on workspace
        if (LauncherAppState.isDisableAllApps()) {
            android.util.Log.e("Launcher3", "verifyApplications()");
            verifyApplications();
        }

        ....
}

 //add for load all app on workspace
private void verifyApplications() {
        final Context context = mApp.getContext();
        ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
        final List<UserHandle> profiles = mUserManager.getUserProfiles();
        for (UserHandle user : profiles) {
            final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
            ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo> added = new ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo>();
            synchronized (this) {
                for (LauncherActivityInfo app : apps) {
                    InstallShortcutReceiver.PendingInstallShortcutInfo pendingInstallShortcutInfo = new InstallShortcutReceiver.PendingInstallShortcutInfo(app, context);
                    added.add(pendingInstallShortcutInfo);
                    installQueue.add(pendingInstallShortcutInfo.getItemInfo());
                }
            }
            if (!added.isEmpty()) {
                mApp.getModel().addAndBindAddedWorkspaceItems(installQueue);
            }
        }
    }

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\model\BaseModelUpdateTask.java

註釋 run() 中的 return

@Override
    public final void run() {
        if (!mModel.isModelLoaded()) {
            if (DEBUG_TASKS) {
                Log.d(TAG, "Ignoring model task since loader is pending=" + this);
            }
            // Loader has not yet run.
            //annotaion for load all app on workspace
            // return;
        }
        execute(mApp, mDataModel, mAllAppsList);
    }

5、新安裝的 app 自動添加圖標到 Workspace

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\model\PackageUpdatedTask.java

execute() 中增加判斷,添加 updateToWorkSpace()

public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {

    ....

     final ArrayList<AppInfo> addedOrModified = new ArrayList<>();
    addedOrModified.addAll(appsList.added);
        
    //add for load new install app on workspace 
    if (LauncherAppState.isDisableAllApps()) {
        android.util.Log.e("cczLauncher3", "updateToWorkSpace()");
        updateToWorkSpace(context, app, appsList);
    }
    
    ...
}

//add for load new install app on workspace
public void updateToWorkSpace(Context context, LauncherAppState app , AllAppsList appsList){
         ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
        final List<UserHandle> profiles = UserManagerCompat.getInstance(context).getUserProfiles();
        ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo> added 
        = new ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo>();
        
        for (UserHandle user : profiles) {
            final List<LauncherActivityInfo> apps = LauncherAppsCompat.getInstance(context).getActivityList(null, user);
            synchronized (this) {
                for (LauncherActivityInfo info : apps) {
                    for (AppInfo appInfo : appsList.added) {
                        if(info.getComponentName().equals(appInfo.componentName)){
                            InstallShortcutReceiver.PendingInstallShortcutInfo mPendingInstallShortcutInfo 
                            =  new InstallShortcutReceiver.PendingInstallShortcutInfo(info,context);
                            added.add(mPendingInstallShortcutInfo);
                            installQueue.add(mPendingInstallShortcutInfo.getItemInfo());
                        }
                    }
                }
            }
        }
        if (!added.isEmpty()) {
            app.getModel().addAndBindAddedWorkspaceItems(installQueue);
        }
    }

6、替換 Workspace 圖標長按刪除選項為取消

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\DeleteDropTarget.java

在 setTextBasedOnDragSource() 、setControlTypeBasedOnDragSource()、onAccessibilityDrop() 中分別增加判斷是否需要刪除圖標

private void setTextBasedOnDragSource(ItemInfo item) {
        if (!TextUtils.isEmpty(mText)) {
            mText = getResources().getString(item.id != ItemInfo.NO_ID
                    ? R.string.remove_drop_target_label
                    : android.R.string.cancel);
            //add for hide deletedroptarget
            if (LauncherAppState.isDisableAllApps()) {
                android.util.Log.e("Launcher3", "hide delete drop target");
                mText = getResources().getString(isCanDrop(item)
                            ? R.string.remove_drop_target_label
                            : android.R.string.cancel);
            }

            requestLayout();
        }
    }

    private void setControlTypeBasedOnDragSource(ItemInfo item) {
        mControlType = item.id != ItemInfo.NO_ID ? ControlType.REMOVE_TARGET
                : ControlType.CANCEL_TARGET;

        //add for hide deletedroptarget [S]
        if (LauncherAppState.isDisableAllApps()) {
            mControlType = isCanDrop(item) ? ControlType.REMOVE_TARGET
                : ControlType.CANCEL_TARGET;
        }        
    }

public void onAccessibilityDrop(View view, ItemInfo item) {
        // Remove the item from launcher and the db, we can ignore the containerInfo in this call
        // because we already remove the drag view from the folder (if the drag originated from
        // a folder) in Folder.beginDrag()
        //add if juge is need remove item from workspace
        if (!LauncherAppState.isDisableAllApps() || isCanDrop(item)) {
            mLauncher.removeItem(view, item, true /* deleteFromDb */);
            mLauncher.getWorkspace().stripEmptyScreens();
            mLauncher.getDragLayer()
                    .announceForAccessibility(getContext().getString(R.string.item_removed));
        }
}

private boolean isCanDrop(ItemInfo item){
        return !(item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
                item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
}

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\dragndrop\DragController.java

drop() 中增加判斷,取消當前拖拽操作

private void drop(DropTarget dropTarget, Runnable flingAnimation) {
    ....

    boolean accepted = false;
        if (dropTarget != null) {
            dropTarget.onDragExit(mDragObject);
            if (dropTarget.acceptDrop(mDragObject)) {
                if (flingAnimation != null) {
                    flingAnimation.run();
                } else {
                    dropTarget.onDrop(mDragObject, mOptions);
                }
                accepted = true;

                //add for cancel canceldroptarget handle
                if (LauncherAppState.isDisableAllApps() && dropTarget instanceof DeleteDropTarget &&
                        isNeedCancelDrag(mDragObject.dragInfo)) {
                    cancelDrag();
                }
            }
        }
        ...
}

private boolean isNeedCancelDrag(ItemInfo item){
        return (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
                item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
 }

7、屏蔽上拉顯示抽屜頁面手勢

vendor\mediatek\proprietary\packages\apps\Launcher3\quickstep\src\com\android\launcher3\uioverrides\OverviewToAllAppsTouchController.java

canInterceptTouch() 中增加判斷是否直接攔截

@Override
    protected boolean canInterceptTouch(MotionEvent ev) {
        //add for forbidden workspace drag change GradientView alph
        if (LauncherAppState.isDisableAllApps()){
            android.util.Log.e("Launcher3", "canInterceptTouch()");
            return false;
        }  

        if (mCurrentAnimation != null) {
            // If we are already animating from a previous state, we can intercept.
            return true;
        }
        if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
            return false;
        }
        if (mLauncher.isInState(ALL_APPS)) {
            // In all-apps only listen if the container cannot scroll itself
            return mLauncher.getAppsView().shouldContainerScroll(ev);
        } else if (mLauncher.isInState(NORMAL)) {
            return true;
        } else if (mLauncher.isInState(OVERVIEW)) {
            RecentsView rv = mLauncher.getOverviewPanel();
            return ev.getY() > (rv.getBottom() - rv.getPaddingBottom());
        } else {
            return false;
        }
    }

8、修改頁面指示線為圓點

vendor\mediatek\proprietary\packages\apps\Launcher3\res\layout\launcher.xml

WorkspacePageIndicator 改為 PageIndicatorDots

<com.android.launcher3.pageindicators.PageIndicatorDots
            android:id="@+id/page_indicator"
            android:layout_width="match_parent"
            android:layout_height="4dp"
            android:layout_gravity="bottom|center_horizontal"
            android:theme="@style/HomeScreenElementTheme" />

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\pageindicators\PageIndicatorDots.java

增加 PageIndicatorDots 繼承 Insettable,覆寫setInsets(), 調整圓點的位置

public class PageIndicatorDots extends View implements PageIndicator, Insettable {

// add for change WorkspacePageIndicator line to dot
    @Override
    public void setInsets(Rect insets) {
        DeviceProfile grid = mLauncher.getDeviceProfile();
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();

        if (grid.isVerticalBarLayout()) {
            Rect padding = grid.workspacePadding;
            lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;
            lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;
            lp.bottomMargin = padding.bottom;
        } else {
            lp.leftMargin = lp.rightMargin = 0;
            lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
            lp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;
        }
        setLayoutParams(lp);
    }

    @Override
    public void setScroll(int currentScroll, int totalScroll) {
        if (mNumPages > 1) {
            if (mIsRtl) {
                currentScroll = totalScroll - currentScroll;
            }
            int scrollPerPage = totalScroll / (mNumPages - 1);

            // add for change WorkspacePageIndicator line to dot
            if (scrollPerPage == 0) {
                return;
            }
            int pageToLeft = currentScroll / scrollPerPage;
            int pageToLeftScroll = pageToLeft * scrollPerPage;
            int pageToRightScroll = pageToLeftScroll + scrollPerPage;

        ...

}

vendor\mediatek\proprietary\packages\apps\Launcher3\src\com\android\launcher3\states\SpringLoadedState.java

註釋 setShouldAutoHide(),避免長按 workSpace 時發生崩潰

@Override
    public void onStateEnabled(Launcher launcher) {
        Workspace ws = launcher.getWorkspace();
        ws.showPageIndicatorAtCurrentScroll();
        //annotaion for WorkspacePageIndicator line to dot
        // ws.getPageIndicator().setShouldAutoHide(false);

        // Prevent any Un/InstallShortcutReceivers from updating the db while we are
        // in spring loaded mode
        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP);
        launcher.getRotationHelper().setCurrentStateRequest(REQUEST_LOCK);
    }

    @Override
    public void onStateDisabled(final Launcher launcher) {
        //annotaion for WorkspacePageIndicator line to dot
        // launcher.getWorkspace().getPageIndicator().setShouldAutoHide(true);

        // Re-enable any Un/InstallShortcutReceiver and now process any queued items
        InstallShortcutReceiver.disableAndFlushInstallQueue(
                InstallShortcutReceiver.FLAG_DRAG_AND_DROP, launcher);
    }

歡迎關註我的英文公眾號【十句英文】,每日1首英文金曲+10句英文,伴你共同進步。

微信掃一掃下方二維碼即可關註:

ME71HS.jpg


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 相關資料:https://zhuanlan.zhihu.com/p/70701037 https://zhuanlan.zhihu.com/p/59061106 一、事務。 1、概念。事務是由一組SQL語句組成的邏輯處理單元。 2、事務的屬性(ACID)。 i:原子性(atomicity):事務是最 ...
  • 算是 ...
  • 統計信息概念 MySQL統計信息是指資料庫通過採樣、統計出來的表、索引的相關信息,例如,表的記錄數、聚集索引page個數、欄位的Cardinality....。MySQL在生成執行計劃時,需要根據索引的統計信息進行估算,計算出最低代價(或者說是最小開銷)的執行計劃.MySQL支持有限的索引統計信息,... ...
  • 1.序列 設置主鍵自增 (1)創建t_user表 create table t_user( id number, name varchar2(10) ); (2)自增設置create sequence my_seq increment by 1 start with 1 (3)序列第一次使用必須使用 ...
  • 在上篇筆記中,為車輛信息表、車輛耗損表以及車輛營收表插入了一些數據。之後便是查詢了,重點也在查詢……按照之前定好的數據結構,如果是查詢mongodb document的最外層比較簡單,但是我們的重點應該是FormItems表單項集合中的表單項,對他們進行查詢、統計等。根據插入的數據,我寫了一個查詢: ...
  • --查詢資料庫鎖表記錄 select sess.sid, sess.serial#, lo.oracle_username, lo.os_user_name, ao.object_name, lo.locked_mode from v$locked_object lo, dba_objects ao ...
  • begin try 語句 end trybegin catch --ERROR_NUMBER() 返回錯誤號。 --ERROR_SEVERITY() 返回嚴重性。 --ERROR_STATE() 返回錯誤狀態號。 --ERROR_PROCEDURE() 返回出現錯誤的存儲過程或觸發器的名稱。 --E ...
  • 跨平臺開發是當下最受歡迎、應用最廣泛的框架之一。能實現跨平臺開發的框架也五花八門,讓人眼花繚亂。最流行的跨平臺框架有 Xamarin、PhoneGap、Ionic、Titanium、Monaca、Sencha、jQuery Mobile、React native、Flutter 等等。但這些工具的表 ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...