Launcher中增加主题的更换(图标、壁纸和锁屏壁纸)–2

1、整体思路

1.1、桌面长按弹出小控件,加上一个切换主题的按钮

1.2、在主题控制界面布局主题切换按钮

1.3、将准备好的图标资源加载进模块(drawable)

1.4、找到桌面图标显示类

1.5、在布局桌面图标方法中加上逻辑

1.6、将准备的图标替换原图标

2、实现

2.1、通过在strings.xml进行壁纸的id的全局搜索 wallpaper_button_text

2.2、发现在OptionsPopupView.java类中的getOptions()方法

/*** Returns the list of supported actions*/public static ArrayList<OptionItem> getOptions(Launcher launcher) {ArrayList<OptionItem> options = new ArrayList<>();options.add(new OptionItem(launcher,  R.string.settings_button_text, // ==> 标题R.drawable.ic_setting,           // ==> 标题左侧图标LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS, // ==> 桌面事件OptionsPopupView::startSettings));  // ==> 点击后是实现的方法/跳转if (!WidgetsModel.GO_DISABLE_WIDGETS) {options.add(new OptionItem(launcher,R.string.widget_button_text,R.drawable.ic_widget,LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS,OptionsPopupView::onWidgetsClicked));}int resString = Utilities.existsStyleWallpapers(launcher) ?R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text;int resDrawable = Utilities.existsStyleWallpapers(launcher) ?R.drawable.ic_palette : R.drawable.ic_wallpaper;options.add(new OptionItem(launcher,resString,resDrawable,IGNORE,OptionsPopupView::startWallpaperPicker));return options;}

2.3、在原代码的基础上加上一个修改主题的部件,代码如下:

options.add(new OptionItem(launcher,R.string.custom_theme_text,R.drawable.ic_customsize_theme,IGNORE,OptionsPopupView::startThemeSettings));

2.4、编写跳转到主题切换页面的方法

/*** Click to jump to the topic section page* @param v* @return*/private static boolean startThemeSettings(View v){Launcher launcher = Launcher.getLauncher(v.getContext());launcher.startActivity(new Intent("android.intent.action.SET_THEME").setPackage(launcher.getPackageName()).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));return true;}

2.5、需要在AndroidManifest-common.xml中注册Activity

<!-- Register a new theme activity --><activityandroid:name="com.android.launcher3.settings.ThemeSettings"android:label="Set Theme"android:theme="@style/HomeSettingsTheme"android:exported="true"android:autoRemoveFromRecents="true"><intent-filter><action android:name="android.intent.action.SET_THEME"/><category android:name="android.intent.category.DEFAULT"/></intent-filter></activity>

2.6、主题控制代码

创建布局文件theme_settings_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><Toolbarandroid:layout_width="match_parent"android:layout_height="wrap_content"android:title="Settings_theme" /><ImageViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:src="@mipmap/wallpaper_03"android:layout_weight="9"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="100dp"android:layout_weight="1"android:orientation="horizontal"><Buttonandroid:id="@+id/modify_theme"android:layout_width="wrap_content"android:layout_height="50dp"android:text="Modify_Theme"android:layout_gravity="center"android:gravity="center"android:layout_weight="1"/><TextViewandroid:layout_width="5dp"android:layout_height="wrap_content"android:layout_gravity="center"android:gravity="center"/><Buttonandroid:id="@+id/default_theme"android:layout_width="wrap_content"android:layout_height="50dp"android:text="Default_Theme"android:layout_gravity="center"android:gravity="center"android:layout_weight="1"/></LinearLayout></LinearLayout>

自定义java代码 ThemeSettings 继承自 FragmentActivity ,代码如下:

/** Copyright (C) 2015 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.android.launcher3.settings;import android.annotation.SuppressLint;
import android.app.WallpaperManager;
import android.content.SharedPreferences;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;import com.android.launcher3.R;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;import java.io.IOException;/*** Change the theme control page*/
public class ThemeSettings extends FragmentActivity implements View.OnClickListener {private TextView modify_theme,default_theme;private WallpaperManager wallpaperManager;private SharedPreferences sp;private SharedPreferences.Editor editor;private Context mContext;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.theme_settings_layout);mContext = this;init();}public void init(){wallpaperManager = (WallpaperManager) getSystemService(mContext.WALLPAPER_SERVICE);modify_theme = (TextView) findViewById(R.id.modify_theme);default_theme = (TextView) findViewById(R.id.default_theme);modify_theme.setOnClickListener(this);default_theme.setOnClickListener(this);if (sp == null){sp = getSharedPreferences("theme_index_name",MODE_PRIVATE);if (sp.getInt("theme_index",-1) == -1){editor=sp.edit();editor.putInt("theme_index",3);editor.apply();}}}public void setModify_theme(){try {editor = sp.edit();editor.putInt("theme_index",1);editor.apply();wallpaperManager.setResource(R.mipmap.wallpaper_03);} catch (IOException e) {e.printStackTrace();}}public void setDefault_theme(){try {editor = sp.edit();editor.putInt("theme_index",3);editor.apply();wallpaperManager.setResource(R.mipmap.default_wallpaper);} catch (IOException e) {e.printStackTrace();}}@Overridepublic void onClick(View view) {if (view.getId()==R.id.modify_theme){setModify_theme();finish();}else if (view.getId() == R.id.default_theme){setDefault_theme();finish();}}
}

2.7、在BubbleTextView类种添加逻辑识别主题

**参考内容:https://blog.csdn.net/CGTAIHYQ/article/details/122047301?spm=1001.2101.3001.6650.5&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-5-122047301-blog-112762072.pc_relevant_blogantidownloadv1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-5-122047301-blog-112762072.pc_relevant_blogantidownloadv1 **

BubbleTextView类是Launcher所有图标文字显示的父类,包括文字的大小,文字刷新的父类。其中的applyIconAndLabel方法将图标加载到桌面布局中。在方法体中setIcon(iconDrawable);代码之前加上关键代码

    @UiThreadprotected void applyIconAndLabel(ItemInfoWithIcon info) {boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER;//    FastBitmapDrawable iconDrawable = info.newIcon(getContext(), useTheme);sharedPreferences=getContext().getSharedPreferences("theme_index_name",Context.MODE_PRIVATE);int a = sharedPreferences.getInt("theme_index",-1);String[] appPackageAndClassName = getContext().getResources().getStringArray(R.array.change_icon_package_name);if (a==-1 || a==3){iconDrawable = info.newIcon(getContext(), useTheme);}else{changThemeOfIcon(appPackageAndClassName,info,useTheme);}mDotParams.color = IconPalette.getMutedColor(iconDrawable.getIconColor(), 0.54f);setIcon(iconDrawable);applyLabel(info);}

**changThemeOfIcon()**方法

private void changThemeOfIcon(String[] appPackageAndClassName,ItemInfoWithIcon info,boolean useTheme){Drawable drawable = pkgAndIcon.get(info.getTargetPackage().toString());if (drawable!=null){BitmapDrawable bd = (BitmapDrawable) drawable;Bitmap bitmap = bd.getBitmap();iconDrawable = new FastBitmapDrawable(bitmap);}else{iconDrawable = info.newIcon(getContext(), useTheme);}}

2.8、将主题内需要更换图标的应用包写入string.xml资源文件,便于图标与包名一一对应。

   <!-- Settings PackageName --><string-array name="change_icon_package_name"><item>com.google.android.dialer</item><item>com.google.android.apps.messaging</item><item>com.google.android.apps.maps</item><item>com.android.chrome</item><item>com.google.android.gm</item><item>com.google.android.apps.photos</item><item>com.google.android.calculator</item><item>com.google.android.calendar</item><item>com.android.settings</item><item>com.google.android.deskclock</item><item>com.google.android.contacts</item><item>com.google.android.apps.nbu.files</item><item>com.google.android.apps.docs</item><item>com.android.fmradio</item><item>com.google.android.apps.googleassistant</item><item>com.google.android.videos</item><item>com.google.android.youtube</item><!--        <item>com.android.chrome</item>--><!--        <item>com.android.settings</item>--><!--        <item>com.android.vending</item>--><!--        <item>com.google.android.apps.docs</item>--><!--        <item>com.google.android.apps.maps</item>--><!--        <item>com.google.android.apps.messaging</item>--><!--        <item>com.google.android.apps.photos</item>--><!--        <item>com.google.android.apps.tachyon</item>--><!--        <item>com.google.android.apps.youtube.music</item>--><!--        <item>com.google.android.calculator</item>--><!--        <item>com.google.android.calendar</item>--><!--        <item>com.google.android.contacts</item>--><!--        <item>com.google.android.deskclock</item>--><!--        <item>com.google.android.dialer</item>--><!--        <item>com.google.android.gm</item>--><!--        <item>com.google.android.videos</item>--><!--        <item>com.google.android.youtube</item>--><!--        <item>com.android.fmradio</item>--><!--        <item>com.android.stk</item>--><!--        <item>com.bullitt.catphones.register.app</item>--><!--        <item>com.bullitt.support</item>--><!--        <item>com.debug.loggerui</item>--><!--        <item>com.device.performance</item>--><!--        <item>com.google.android.apps.googleassistant</item>--><!--        <item>com.google.android.apps.nbu.files</item>--><!--        <item>com.google.android.googlequicksearchbox</item>--><!--        <item>com.google.android.keep</item>--><!--        <item>com.mediatek.gnss.nonframeworklbs</item>--><!--        <item>com.mobisystems.office</item>--><!--        <item>com.st.nfc.dta</item>--><!--        <item>com.toolbox.app</item>--><!--        <item>com.tinno.lancher3demo</item>--></string-array>

获取图标资源的方法

private Map<String,Drawable> getDrawableRes(){ArrayList<Drawable> drawables=new ArrayList<>();drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_dialer));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_app_messaging));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_app_maps));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_chrome));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_gmail));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_app_photo));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_calculator));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_calendar));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.android_settings));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_clock));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_contacts));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_app_files));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_app_docs));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.android_fmradio));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_app_assistant));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_video));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_youtube));String[] appPackageAndClassName = getContext().getResources().getStringArray(R.array.change_icon_package_name);Map<String,Drawable> pkgAndIcon=new HashMap<String,Drawable>();for (int i = 0; i < appPackageAndClassName.length; i++) {pkgAndIcon.put(appPackageAndClassName[i],drawables.get(i));}return pkgAndIcon;}
private SharedPreferences sharedPreferences;
private FastBitmapDrawable iconDrawable;
// Get the map corresponding to the entire application name and icon
private Map<String,Drawable> pkgAndIcon=getDrawableRes();

BubbleTextView.java类完整代码:

/** Copyright (C) 2008 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.android.launcher3;import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.Property;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.widget.TextView;import androidx.annotation.Nullable;
import androidx.annotation.UiThread;import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.icons.PlaceHolderIconDrawable;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.model.data.SearchActionItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.IconLabelDotView;import androidx.core.content.ContextCompat;
import android.content.SharedPreferences;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.Bitmap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;import java.text.NumberFormat;/*** TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan* because we want to make the bubble taller than the text and TextView's clip is* too aggressive.*/
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,IconLabelDotView, DraggableView, Reorderable {private SharedPreferences sharedPreferences;private FastBitmapDrawable iconDrawable;// Get the map corresponding to the entire application name and iconprivate Map<String,Drawable> pkgAndIcon=getDrawableRes();private static final int DISPLAY_WORKSPACE = 0;private static final int DISPLAY_ALL_APPS = 1;private static final int DISPLAY_FOLDER = 2;protected static final int DISPLAY_TASKBAR = 5;private static final int DISPLAY_SEARCH_RESULT = 6;private static final int DISPLAY_SEARCH_RESULT_SMALL = 7;private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};private static final float HIGHLIGHT_SCALE = 1.16f;private final PointF mTranslationForReorderBounce = new PointF(0, 0);private final PointF mTranslationForReorderPreview = new PointF(0, 0);private float mScaleForReorderBounce = 1f;private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY= new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {@Overridepublic Float get(BubbleTextView bubbleTextView) {return bubbleTextView.mDotParams.scale;}@Overridepublic void set(BubbleTextView bubbleTextView, Float value) {bubbleTextView.mDotParams.scale = value;bubbleTextView.invalidate();}};public static final Property<BubbleTextView, Float> TEXT_ALPHA_PROPERTY= new Property<BubbleTextView, Float>(Float.class, "textAlpha") {@Overridepublic Float get(BubbleTextView bubbleTextView) {return bubbleTextView.mTextAlpha;}@Overridepublic void set(BubbleTextView bubbleTextView, Float alpha) {bubbleTextView.setTextAlpha(alpha);}};private final ActivityContext mActivity;private FastBitmapDrawable mIcon;private boolean mCenterVertically;protected final int mDisplay;private final CheckLongPressHelper mLongPressHelper;private final boolean mLayoutHorizontal;private final int mIconSize;@ViewDebug.ExportedProperty(category = "launcher")private boolean mIsIconVisible = true;@ViewDebug.ExportedProperty(category = "launcher")private int mTextColor;@ViewDebug.ExportedProperty(category = "launcher")private float mTextAlpha = 1;@ViewDebug.ExportedProperty(category = "launcher")private DotInfo mDotInfo;private DotRenderer mDotRenderer;@ViewDebug.ExportedProperty(category = "launcher", deepExport = true)protected DotRenderer.DrawParams mDotParams;private Animator mDotScaleAnim;private boolean mForceHideDot;@ViewDebug.ExportedProperty(category = "launcher")private boolean mStayPressed;@ViewDebug.ExportedProperty(category = "launcher")private boolean mIgnorePressedStateChange;@ViewDebug.ExportedProperty(category = "launcher")private boolean mDisableRelayout = false;private HandlerRunnable mIconLoadRequest;private boolean mEnableIconUpdateAnimation = false;public BubbleTextView(Context context) {this(context, null, 0);}public BubbleTextView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);mActivity = ActivityContext.lookupContext(context);TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.BubbleTextView, defStyle, 0);mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);DeviceProfile grid = mActivity.getDeviceProfile();mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);final int defaultIconSize;if (mDisplay == DISPLAY_WORKSPACE) {setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);setCompoundDrawablePadding(grid.iconDrawablePaddingPx);defaultIconSize = grid.iconSizePx;setCenterVertically(grid.isScalableGrid);} else if (mDisplay == DISPLAY_ALL_APPS) {setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);defaultIconSize = grid.allAppsIconSizePx;} else if (mDisplay == DISPLAY_FOLDER) {setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);defaultIconSize = grid.folderChildIconSizePx;} else if (mDisplay == DISPLAY_SEARCH_RESULT) {defaultIconSize = getResources().getDimensionPixelSize(R.dimen.search_row_icon_size);} else if (mDisplay == DISPLAY_SEARCH_RESULT_SMALL) {defaultIconSize = getResources().getDimensionPixelSize(R.dimen.search_row_small_icon_size);} else if (mDisplay == DISPLAY_TASKBAR) {defaultIconSize = grid.iconSizePx;} else {// widget_selection or shortcut_popupdefaultIconSize = grid.iconSizePx;}mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride,defaultIconSize);a.recycle();mLongPressHelper = new CheckLongPressHelper(this);mDotParams = new DotRenderer.DrawParams();setEllipsize(TruncateAt.END);setAccessibilityDelegate(mActivity.getAccessibilityDelegate());setTextAlpha(1f);}@Overrideprotected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {// Disable marques when not focused to that, so that updating text does not cause relayout.setEllipsize(focused ? TruncateAt.MARQUEE : TruncateAt.END);super.onFocusChanged(focused, direction, previouslyFocusedRect);}/*** Resets the view so it can be recycled.*/public void reset() {mDotInfo = null;mDotParams.color = Color.TRANSPARENT;cancelDotScaleAnim();mDotParams.scale = 0f;mForceHideDot = false;setBackground(null);}private void cancelDotScaleAnim() {if (mDotScaleAnim != null) {mDotScaleAnim.cancel();}}private void animateDotScale(float... dotScales) {cancelDotScaleAnim();mDotScaleAnim = ObjectAnimator.ofFloat(this, DOT_SCALE_PROPERTY, dotScales);mDotScaleAnim.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {mDotScaleAnim = null;}});mDotScaleAnim.start();}@UiThreadpublic void applyFromWorkspaceItem(WorkspaceItemInfo info) {applyFromWorkspaceItem(info, false);}@Overridepublic void setAccessibilityDelegate(AccessibilityDelegate delegate) {if (delegate instanceof LauncherAccessibilityDelegate) {super.setAccessibilityDelegate(delegate);} else {// NO-OP// Workaround for b/129745295 where RecyclerView is setting our Accessibility// delegate incorrectly. There are no cases when we shouldn't be using the// LauncherAccessibilityDelegate for BubbleTextView.}}@UiThreadpublic void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {applyIconAndLabel(info);setTag(info);applyLoadingState(promiseStateChanged);applyDotState(info, false /* animate */);setDownloadStateContentDescription(info, info.getProgressLevel());}@UiThreadpublic void applyFromApplicationInfo(AppInfo info) {applyIconAndLabel(info);// We don't need to check the info since it's not a WorkspaceItemInfosuper.setTag(info);// Verify high res immediatelyverifyHighRes();if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {applyProgressLevel();}applyDotState(info, false /* animate */);setDownloadStateContentDescription(info, info.getProgressLevel());}/*** Apply label and tag using a generic {@link ItemInfoWithIcon}*/@UiThreadpublic void applyFromItemInfoWithIcon(ItemInfoWithIcon info) {applyIconAndLabel(info);// We don't need to check the info since it's not a WorkspaceItemInfosuper.setTag(info);// Verify high res immediatelyverifyHighRes();setDownloadStateContentDescription(info, info.getProgressLevel());}/*** Apply label and tag using a {@link SearchActionItemInfo}*/@UiThreadpublic void applyFromSearchActionItemInfo(SearchActionItemInfo searchActionItemInfo) {applyIconAndLabel(searchActionItemInfo);setTag(searchActionItemInfo);}@UiThreadprotected void applyIconAndLabel(ItemInfoWithIcon info) {boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER;//    FastBitmapDrawable iconDrawable = info.newIcon(getContext(), useTheme);sharedPreferences=getContext().getSharedPreferences("theme_index_name",Context.MODE_PRIVATE);int a = sharedPreferences.getInt("theme_index",-1);String[] appPackageAndClassName = getContext().getResources().getStringArray(R.array.change_icon_package_name);if (a==-1 || a==3){iconDrawable = info.newIcon(getContext(), useTheme);}else{changThemeOfIcon(appPackageAndClassName,info,useTheme);}mDotParams.color = IconPalette.getMutedColor(iconDrawable.getIconColor(), 0.54f);setIcon(iconDrawable);applyLabel(info);}private void changThemeOfIcon(String[] appPackageAndClassName,ItemInfoWithIcon info,boolean useTheme){Drawable drawable = pkgAndIcon.get(info.getTargetPackage().toString());if (drawable!=null){BitmapDrawable bd = (BitmapDrawable) drawable;Bitmap bitmap = bd.getBitmap();iconDrawable = new FastBitmapDrawable(bitmap);}else{iconDrawable = info.newIcon(getContext(), useTheme);}}private Map<String,Drawable> getDrawableRes(){ArrayList<Drawable> drawables=new ArrayList<>();drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_dialer));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_app_messaging));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_app_maps));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_chrome));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_gmail));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_app_photo));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_calculator));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_calendar));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.android_settings));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_clock));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_contacts));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_app_files));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_app_docs));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.android_fmradio));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_app_assistant));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_video));drawables.add(ContextCompat.getDrawable(getContext(), R.mipmap.google_youtube));String[] appPackageAndClassName = getContext().getResources().getStringArray(R.array.change_icon_package_name);Map<String,Drawable> pkgAndIcon=new HashMap<String,Drawable>();for (int i = 0; i < appPackageAndClassName.length; i++) {pkgAndIcon.put(appPackageAndClassName[i],drawables.get(i));}return pkgAndIcon;}@UiThreadprivate void applyLabel(ItemInfoWithIcon info) {setText(info.title);if (info.contentDescription != null) {setContentDescription(info.isDisabled()? getContext().getString(R.string.disabled_app_label, info.contentDescription): info.contentDescription);}}/*** Overrides the default long press timeout.*/public void setLongPressTimeoutFactor(float longPressTimeoutFactor) {mLongPressHelper.setLongPressTimeoutFactor(longPressTimeoutFactor);}@Overridepublic void refreshDrawableState() {if (!mIgnorePressedStateChange) {super.refreshDrawableState();}}@Overrideprotected int[] onCreateDrawableState(int extraSpace) {final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);if (mStayPressed) {mergeDrawableStates(drawableState, STATE_PRESSED);}return drawableState;}/** Returns the icon for this view. */public FastBitmapDrawable getIcon() {return mIcon;}@Overridepublic boolean onTouchEvent(MotionEvent event) {// ignore events if they happen in padding areaif (event.getAction() == MotionEvent.ACTION_DOWN&& shouldIgnoreTouchDown(event.getX(), event.getY())) {return false;}if (isLongClickable()) {super.onTouchEvent(event);mLongPressHelper.onTouchEvent(event);// Keep receiving the rest of the eventsreturn true;} else {return super.onTouchEvent(event);}}/*** Returns true if the touch down at the provided position be ignored*/protected boolean shouldIgnoreTouchDown(float x, float y) {return y < getPaddingTop()|| x < getPaddingLeft()|| y > getHeight() - getPaddingBottom()|| x > getWidth() - getPaddingRight();}void setStayPressed(boolean stayPressed) {mStayPressed = stayPressed;refreshDrawableState();}@Overridepublic void onVisibilityAggregated(boolean isVisible) {super.onVisibilityAggregated(isVisible);if (mIcon != null) {mIcon.setVisible(isVisible, false);}}void clearPressedBackground() {setPressed(false);setStayPressed(false);}@Overridepublic boolean onKeyUp(int keyCode, KeyEvent event) {// Unlike touch events, keypress event propagate pressed state change immediately,// without waiting for onClickHandler to execute. Disable pressed state changes here// to avoid flickering.mIgnorePressedStateChange = true;boolean result = super.onKeyUp(keyCode, event);mIgnorePressedStateChange = false;refreshDrawableState();return result;}@SuppressWarnings("wrongcall")protected void drawWithoutDot(Canvas canvas) {super.onDraw(canvas);}@Overridepublic void onDraw(Canvas canvas) {super.onDraw(canvas);drawDotIfNecessary(canvas);}/*** Draws the notification dot in the top right corner of the icon bounds.** @param canvas The canvas to draw to.*/protected void drawDotIfNecessary(Canvas canvas) {if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {getIconBounds(mDotParams.iconBounds);Utilities.scaleRectAboutCenter(mDotParams.iconBounds,IconShape.getNormalizationScale());final int scrollX = getScrollX();final int scrollY = getScrollY();canvas.translate(scrollX, scrollY);mDotRenderer.draw(canvas, mDotParams);canvas.translate(-scrollX, -scrollY);}}@Overridepublic void setForceHideDot(boolean forceHideDot) {if (mForceHideDot == forceHideDot) {return;}mForceHideDot = forceHideDot;if (forceHideDot) {invalidate();} else if (hasDot()) {animateDotScale(0, 1);}}private boolean hasDot() {return mDotInfo != null;}public void getIconBounds(Rect outBounds) {getIconBounds(this, outBounds, mIconSize);}public static void getIconBounds(View iconView, Rect outBounds, int iconSize) {int top = iconView.getPaddingTop();int left = (iconView.getWidth() - iconSize) / 2;int right = left + iconSize;int bottom = top + iconSize;outBounds.set(left, top, right, bottom);}/*** Sets whether to vertically center the content.*/public void setCenterVertically(boolean centerVertically) {mCenterVertically = centerVertically;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if (mCenterVertically) {Paint.FontMetrics fm = getPaint().getFontMetrics();int cellHeightPx = mIconSize + getCompoundDrawablePadding() +(int) Math.ceil(fm.bottom - fm.top);int height = MeasureSpec.getSize(heightMeasureSpec);setPadding(getPaddingLeft(), (height - cellHeightPx) / 2, getPaddingRight(),getPaddingBottom());}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overridepublic void setTextColor(int color) {mTextColor = color;super.setTextColor(getModifiedColor());}@Overridepublic void setTextColor(ColorStateList colors) {mTextColor = colors.getDefaultColor();if (Float.compare(mTextAlpha, 1) == 0) {super.setTextColor(colors);} else {super.setTextColor(getModifiedColor());}}public boolean shouldTextBeVisible() {// Text should be visible everywhere but the hotseat.Object tag = getParent() instanceof FolderIcon ? ((View) getParent()).getTag() : getTag();ItemInfo info = tag instanceof ItemInfo ? (ItemInfo) tag : null;return info == null || (info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT&& info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION);}public void setTextVisibility(boolean visible) {setTextAlpha(visible ? 1 : 0);}private void setTextAlpha(float alpha) {mTextAlpha = alpha;super.setTextColor(getModifiedColor());}private int getModifiedColor() {if (mTextAlpha == 0) {// Special case to prevent text shadows in high contrast modereturn Color.TRANSPARENT;}return setColorAlphaBound(mTextColor, Math.round(Color.alpha(mTextColor) * mTextAlpha));}/*** Creates an animator to fade the text in or out.** @param fadeIn Whether the text should fade in or fade out.*/public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {float toAlpha = shouldTextBeVisible() && fadeIn ? 1 : 0;return ObjectAnimator.ofFloat(this, TEXT_ALPHA_PROPERTY, toAlpha);}@Overridepublic void cancelLongPress() {super.cancelLongPress();mLongPressHelper.cancelLongPress();}/*** Applies the loading progress value to the progress bar.** If this app is installing, the progress bar will be updated with the installation progress.* If this app is installed and downloading incrementally, the progress bar will be updated* with the total download progress.*/public void applyLoadingState(boolean promiseStateChanged) {if (getTag() instanceof ItemInfoWithIcon) {WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE)!= 0) {updateProgressBarUi(info.getProgressLevel() == 100);} else if (info.hasPromiseIconUi() || (info.runtimeStatusFlags& ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {updateProgressBarUi(promiseStateChanged);}}}private void updateProgressBarUi(boolean maybePerformFinishedAnimation) {PreloadIconDrawable preloadDrawable = applyProgressLevel();if (preloadDrawable != null && maybePerformFinishedAnimation) {preloadDrawable.maybePerformFinishedAnimation();}}/** Applies the given progress level to the this icon's progress bar. */@Nullablepublic PreloadIconDrawable applyProgressLevel() {if (!(getTag() instanceof ItemInfoWithIcon)) {return null;}ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();int progressLevel = info.getProgressLevel();if (progressLevel >= 100) {setContentDescription(info.contentDescription != null? info.contentDescription : "");} else if (progressLevel > 0) {setDownloadStateContentDescription(info, progressLevel);} else {setContentDescription(getContext().getString(R.string.app_waiting_download_title, info.title));}if (mIcon != null) {PreloadIconDrawable preloadIconDrawable;if (mIcon instanceof PreloadIconDrawable) {preloadIconDrawable = (PreloadIconDrawable) mIcon;preloadIconDrawable.setLevel(progressLevel);preloadIconDrawable.setIsDisabled(!info.isAppStartable());} else {preloadIconDrawable = makePreloadIcon();setIcon(preloadIconDrawable);}return preloadIconDrawable;}return null;}/*** Creates a PreloadIconDrawable with the appropriate progress level without mutating this* object.*/@Nullablepublic PreloadIconDrawable makePreloadIcon() {if (!(getTag() instanceof ItemInfoWithIcon)) {return null;}ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();int progressLevel = info.getProgressLevel();final PreloadIconDrawable preloadDrawable = newPendingIcon(getContext(), info);preloadDrawable.setLevel(progressLevel);preloadDrawable.setIsDisabled(!info.isAppStartable());return preloadDrawable;}public void applyDotState(ItemInfo itemInfo, boolean animate) {if (mIcon instanceof FastBitmapDrawable) {boolean wasDotted = mDotInfo != null;mDotInfo = mActivity.getDotInfoForItem(itemInfo);boolean isDotted = mDotInfo != null;float newDotScale = isDotted ? 1f : 0;if (mDisplay == DISPLAY_ALL_APPS) {mDotRenderer = mActivity.getDeviceProfile().mDotRendererAllApps;} else {mDotRenderer = mActivity.getDeviceProfile().mDotRendererWorkSpace;}if (wasDotted || isDotted) {// Animate when a dot is first added or when it is removed.if (animate && (wasDotted ^ isDotted) && isShown()) {animateDotScale(newDotScale);} else {cancelDotScaleAnim();mDotParams.scale = newDotScale;invalidate();}}if (itemInfo.contentDescription != null) {if (itemInfo.isDisabled()) {setContentDescription(getContext().getString(R.string.disabled_app_label,itemInfo.contentDescription));} else if (hasDot()) {int count = mDotInfo.getNotificationCount();setContentDescription(getContext().getResources().getQuantityString(R.plurals.dotted_app_label, count, itemInfo.contentDescription, count));} else {setContentDescription(itemInfo.contentDescription);}}}}private void setDownloadStateContentDescription(ItemInfoWithIcon info, int progressLevel) {if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK)!= 0) {String percentageString = NumberFormat.getPercentInstance().format(progressLevel * 0.01);if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {setContentDescription(getContext().getString(R.string.app_installing_title, info.title, percentageString));} else if ((info.runtimeStatusFlags& ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) {setContentDescription(getContext().getString(R.string.app_downloading_title, info.title, percentageString));}}}/*** Sets the icon for this view based on the layout direction.*/protected void setIcon(FastBitmapDrawable icon) {if (mIsIconVisible) {applyCompoundDrawables(icon);}mIcon = icon;if (mIcon != null) {mIcon.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);}}@Overridepublic void setIconVisible(boolean visible) {mIsIconVisible = visible;if (!mIsIconVisible) {resetIconScale();}Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);applyCompoundDrawables(icon);}protected boolean iconUpdateAnimationEnabled() {return mEnableIconUpdateAnimation;}protected void applyCompoundDrawables(Drawable icon) {// If we had already set an icon before, disable relayout as the icon size is the// same as before.mDisableRelayout = mIcon != null;icon.setBounds(0, 0, mIconSize, mIconSize);updateIcon(icon);// If the current icon is a placeholder color, animate its update.if (mIcon != null&& mIcon instanceof PlaceHolderIconDrawable&& iconUpdateAnimationEnabled()) {((PlaceHolderIconDrawable) mIcon).animateIconUpdate(icon);}mDisableRelayout = false;}@Overridepublic void requestLayout() {if (!mDisableRelayout) {super.requestLayout();}}/*** Applies the item info if it is same as what the view is pointing to currently.*/@Overridepublic void reapplyItemInfo(ItemInfoWithIcon info) {if (getTag() == info) {mIconLoadRequest = null;mDisableRelayout = true;mEnableIconUpdateAnimation = true;// Optimization: Starting in N, pre-uploads the bitmap to RenderThread.info.bitmap.icon.prepareToDraw();if (info instanceof AppInfo) {applyFromApplicationInfo((AppInfo) info);} else if (info instanceof WorkspaceItemInfo) {applyFromWorkspaceItem((WorkspaceItemInfo) info);mActivity.invalidateParent(info);} else if (info instanceof PackageItemInfo) {applyFromItemInfoWithIcon((PackageItemInfo) info);} else if (info instanceof SearchActionItemInfo) {applyFromSearchActionItemInfo((SearchActionItemInfo) info);}mDisableRelayout = false;mEnableIconUpdateAnimation = false;}}/*** Verifies that the current icon is high-res otherwise posts a request to load the icon.*/public void verifyHighRes() {if (mIconLoadRequest != null) {mIconLoadRequest.cancel();mIconLoadRequest = null;}if (getTag() instanceof ItemInfoWithIcon) {ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();if (info.usingLowResIcon()) {mIconLoadRequest = LauncherAppState.getInstance(getContext()).getIconCache().updateIconInBackground(BubbleTextView.this, info);}}}public int getIconSize() {return mIconSize;}private void updateTranslation() {super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x);super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y);}public void setReorderBounceOffset(float x, float y) {mTranslationForReorderBounce.set(x, y);updateTranslation();}public void getReorderBounceOffset(PointF offset) {offset.set(mTranslationForReorderBounce);}@Overridepublic void setReorderPreviewOffset(float x, float y) {mTranslationForReorderPreview.set(x, y);updateTranslation();}@Overridepublic void getReorderPreviewOffset(PointF offset) {offset.set(mTranslationForReorderPreview);}public void setReorderBounceScale(float scale) {mScaleForReorderBounce = scale;super.setScaleX(scale);super.setScaleY(scale);}public float getReorderBounceScale() {return mScaleForReorderBounce;}public View getView() {return this;}@Overridepublic int getViewType() {return DRAGGABLE_ICON;}@Overridepublic void getWorkspaceVisualDragBounds(Rect bounds) {DeviceProfile grid = mActivity.getDeviceProfile();BubbleTextView.getIconBounds(this, bounds, grid.iconSizePx);}private int getIconSizeForDisplay(int display) {DeviceProfile grid = mActivity.getDeviceProfile();switch (display) {case DISPLAY_ALL_APPS:return grid.allAppsIconSizePx;case DISPLAY_WORKSPACE:case DISPLAY_FOLDER:default:return grid.iconSizePx;}}public void getSourceVisualDragBounds(Rect bounds) {BubbleTextView.getIconBounds(this, bounds, getIconSizeForDisplay(mDisplay));}@Overridepublic SafeCloseable prepareDrawDragView() {resetIconScale();setForceHideDot(true);return () -> { };}private void resetIconScale() {if (mIcon instanceof FastBitmapDrawable) {((FastBitmapDrawable) mIcon).resetScale();}}private void updateIcon(Drawable newIcon) {if (mLayoutHorizontal) {setCompoundDrawablesRelative(newIcon, null, null, null);} else {setCompoundDrawables(null, newIcon, null, null);}}
}

2.9、更改桌面壁纸

Android中的壁纸分为静态壁纸和动态壁纸, 这两类壁纸本质都是一样的, 都是通过继承WallpaperService来实现的,只不过是绘制方面的差异。WallpaperManagerService用于管理壁纸的运行与切换,

并通过WallpaperManager类向外界提供操作壁纸的接口,主要体现了对壁纸的管理方式。WallpaperService则对应壁纸的具体实现,实现壁纸服务相关的核心是WallpaperService中的Engine类。

所以声明WallpaperManager对象,获取到WallpaperManagerService,调用setResource方法就可以实现壁纸的更换。

private WallpaperManager wallpaperManager;
wallpaperManager = (WallpaperManager) getSystemService(mContext.WALLPAPER_SERVICE);
wallpapaerManager.se

Launcher中修改桌面壁纸、锁屏壁纸和手机图标相关推荐

  1. win7锁屏壁纸更换,解除壁纸256Kb限制教程

    参考文献: 百度经验连接:点击打开链接 贴吧某大神贴连接:点击打开链接 今天闲来无事,看着桌面,锁屏壁纸越来越不入眼,正好最近迷恋滑板,就想着把自己的桌面壁纸跟锁屏壁纸弄个滑板,这样心情就美丽啦,哈哈 ...

  2. 10锁屏幻灯片_手机跟我学第一百八十八课——如何设置锁屏

    银 / 发 / 学 /堂 手机跟我学 小米手机设置 各位同学们,大家好,我是小银班长 随着科技的发展,手机已经越来越离不开咱们的日常生活,而且也让我们的生活变得更加便利.我们可以对手机的基础设置进行调 ...

  3. 从C盘深处中获取win10锁屏壁纸保存到D:\LockScreenPicture中并修改为jpg文件

    首先我们要知道win10的锁屏壁纸都保存在 C:\Users\31286\AppData\Local\Packages\Microsoft.Windows.ContentDeliveryManager ...

  4. Windows 聚焦的锁屏壁纸设置为桌面壁纸

    需求: Windows的锁屏壁纸偶尔遇到非常喜欢的壁纸,想设置为桌面壁纸. 步骤如下: 1. "Windows 聚焦"的锁屏壁纸都保存在隐藏文件夹 --- Assets里. a. ...

  5. w7怎么把计算机放桌面壁纸,W7电脑系统怎么更改锁屏壁纸

    w7电脑系统怎么更改锁屏壁纸?很多第三方软件都可以做到,但我们该如何手动设置锁屏壁纸呢?本文将配合使用注册表修改w7电脑系统的锁屏壁纸的步骤进行一个详细的讲解,具体步骤请看下文. w7电脑系统更改锁屏 ...

  6. 怎么更改锁定计算机背景图片,Win7系统怎么更改锁屏壁纸?注册表如何修改锁屏壁纸?...

    Win7系统怎么更改锁屏壁纸?很多第三方软件都可以做到,但我们该如何手动设置锁屏壁纸呢?本文将配合使用注册表修改Win7系统的锁屏壁纸的步骤进行一个详细的讲解,具体步骤请看下文. Win7系统更改锁屏 ...

  7. 修改win7锁屏壁纸,突破壁纸大小256Kb限制

    1.修改注册表() 推荐大家用第一种修改方式: 复制以下内容到记事本,保存为"1.reg"文件名并双击此reg文件导入注册表: Windows Registry Editor Ve ...

  8. windows技巧--win7修改锁屏壁纸(不用软件)

    今天看电脑的锁屏壁纸感觉不好玩,就自己在网上找了一下修改电脑锁屏壁纸的方法,搜索结果如下: 1. 进入注册表(在运行框中输入regedit),找到 "HKEY_LOCAL_MACHINE\S ...

  9. python win10 桌面_实战 | Python批量提取Win10锁屏壁纸

    使用Win10的朋友会发现,每次开机锁屏界面都会有不一样的漂亮图片,这些图片通常选自优秀的摄影作品,十分精美.但是由于系统会自动更换这些图片,所以就算再好看的图片,也许下次开机之后就被替换掉了. 借助 ...

最新文章

  1. 适配iOS 13 tabbar 标题字体不显示以及返回变蓝色的为问题
  2. Android 广播的生命周期
  3. 成功的自动化测试实施的5大支柱(译)
  4. 内核功能导致重启_诊断修复 TiDB Operator 在 K8s 测试中遇到的 Linux 内核问题
  5. CriminalIntent项目的强大完善
  6. 前端学习(1899)vue之电商管理系统电商系统之渲染添加用户的表单
  7. mysql数据库基础的简单操作指南
  8. python 模块 chardet下载方法及介绍
  9. Jn建站系统2.0源码 附视频安装教程
  10. es 模糊查询_【ES 系列5】ES 查询优化
  11. USB控制相关批处理
  12. EasyX画动态时钟
  13. 学习笔记2-面包板的使用
  14. c语言百分号-5.2f什么意思,c语言中的%d跟%5.2f有什么区别
  15. 有什么办法可以预防网页被劫持
  16. ffmpeg裁剪视频尺寸
  17. 为什么说 NLP 将是未来数据领域的珠峰?
  18. python遇到天猫反爬虫_selenium 淘宝登入反爬虫解决方案(亲测有效)
  19. 分享50个免费的云盘网盘服务——拥有无限储存空间
  20. 拥抱AI!人工智能如何在疫情期间重塑娱乐业?

热门文章

  1. 浏览器记住密码后,密码框自动填充功能禁用
  2. 我的第一个油猴脚本「屏蔽CSDN底下的登录栏」
  3. 饿了么零售开放平台-.Net Core
  4. 寻找几种常见的推广模式
  5. FISCO-BCOS学习——单群组4节点联盟链、控制台 搭建
  6. MySQL PK,FK,视图,索引,引擎总结【12000字概览MySQL】用于查漏补缺
  7. 国内10大物联网公司排行榜,求职必备‼️
  8. 电影票房之数据分析(Hive)-- 第2关
  9. 用Jdk自带工具keytool生成受信任的证书
  10. C语言:数字三角形!