android 8.1 Launcher添加切换主题功能

实现思路:APK包的方式,将主题资源放在Android工程上通过打包安装实现主题的替换。

修改方法:
一:面板添加theme控件
在这里插入图片描述
修改Launcher3/res/layout/overview_panel.xml

<!-- zrx add start -->
      <TextView
        android:id="@+id/theme_button"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:drawablePadding="4dp"
        android:drawableTop="@drawable/ic_setting"
        android:drawableTint="?attr/workspaceTextColor"
        android:fontFamily="sans-serif-condensed"
        android:gravity="center_horizontal"
        android:stateListAnimator="@animator/overview_button_anim"
        android:text="@string/theme_button_text"
        android:textAllCaps="true"
        android:textColor="?attr/workspaceTextColor"
        android:textSize="12sp" />
      <!-- zrx add end -->

二:点击theme控件进入展示主题列表界面
在这里插入图片描述
layout布局
添加界面的布局theme_pick.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true" >

    <LinearLayout
        android:id="@+id/theme_title_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:visibility="gone" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:clickable="true" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/theme_title" />
    </LinearLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/theme_title_layout"
        android:background="@android:color/white" >
    </android.support.v7.widget.RecyclerView>

    <TextView
        android:id="@+id/theme_apply"
        android:layout_width="120dp"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@android:color/white"
        android:clickable="true"
        android:gravity="center"
        android:text="@string/theme_apply" />

</RelativeLayout>

添加子item的布局recycle_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="100dp"
    android:layout_height="150dp"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="132dp" >

        <ImageView
            android:id="@+id/recycler_view_item_icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:scaleType="fitXY" />

        <CheckBox
            android:id="@+id/theme_item_check"
            android:layout_width="22dp"
            android:layout_height="22dp"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_marginRight="5dp"
            android:background="@drawable/theme_selector"
            android:button="@null"
            android:visibility="gone" />
    </RelativeLayout>

    <TextView
        android:id="@+id/recycler_view_item_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:textColor="@android:color/black" />

</LinearLayout>

三:对主题列表界面的处理
在这里插入图片描述
新建ThemePickActivity.java

package com.android.launcher3.theme;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.util.ArrayList;
import com.android.launcher3.R;
import android.content.Intent;
import com.android.launcher3.Launcher;

/**
 * 
 * @author zrx add
 *
 */
public class ThemePickActivity extends Activity {
	private RecyclerView mRecyclerView;
    private PackageManager pm;
    private boolean checkState = false;
    private boolean checkLongState = false;
    private RecycleViewAdapter recycleViewAdapter;
    private TextView mApplyTextView;
    private String mCurrentThemeName;
    private IStateClearCallback mStateClearCallback;
    private  static  final String DEFAULT_THEME_PACHKAGE = "com.zrx.default";
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.theme_pick);
		
		String currentThemeName = ThemeSharePerfence.getThemeShareprefValue(this);
		if(currentThemeName != null){
			checkState = true;
			mCurrentThemeName = currentThemeName;
		}else{
			mCurrentThemeName = DEFAULT_THEME_PACHKAGE;
		}
		initViews();
		pm = getPackageManager();
	}
	
	@Override
	public void onBackPressed() {
		// TODO Auto-generated method stub
		if(mStateClearCallback != null){
			mStateClearCallback.doClearWithHearts();
			mStateClearCallback = null;
			return;
		}
		super.onBackPressed();
	}
	
	private void initViews(){
		mRecyclerView = (RecyclerView)findViewById(R.id.recycler_view);
		GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3);
		int spanCount = 3;
		int spacing = 20;
		boolean includeEdge = true;
		mRecyclerView.addItemDecoration(new GridSpacingItemDecoration(spanCount, spacing, includeEdge));
		// mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
		
		mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));
		recycleViewAdapter = new RecycleViewAdapter(this);
		recycleViewAdapter.setThemes(ThemeContainer.getInstance().getAllThemeList(this));
		mRecyclerView.setAdapter(recycleViewAdapter);
	
		mApplyTextView = (TextView) findViewById(R.id.theme_apply);
		mApplyTextView.setVisibility(View.GONE);
		
	}
	
	private void restartLauncherActivity() {

		android.os.Process.killProcess(android.os.Process.myPid());

		Intent intent = getBaseContext().getPackageManager()
				.getLaunchIntentForPackage(getBaseContext().getPackageName());
		if (intent == null) {
			intent = new Intent(this, Launcher.class);
		}
		intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
				| Intent.FLAG_ACTIVITY_NEW_TASK);
		startActivity(intent);

	}
	
	class RecycleViewAdapter extends RecyclerView.Adapter{
		private ArrayList<Object> themes;
		private ThemeInstaller mThemeInstaller;
		private CheckBox mCurrentCheckBox;
		private ArrayList<ThemeInfo> mDeleteThemeInfos;
		private Context mContext;
		public RecycleViewAdapter(Context context){
			mContext = context;
		}
		
		public void setThemes(ArrayList<Object> themeList){
			if(themeList == null){
				return;
			}
			themes = new ArrayList<Object>();
			ThemeInfo defaultTheme = new ThemeInfo(getString(R.string.default_theme), R.drawable.default_theme_wallpaper);
			defaultTheme.setFlag(ThemeInfo.THEME_FLAG_DEFAULT);
            defaultTheme.setPackageName(DEFAULT_THEME_PACHKAGE);
            themes.add(defaultTheme);
            for (int i = 0; i < themeList.size(); i++)
            {
                themes.add(themeList.get(i));
            }
            mThemeInstaller = new ThemeInstaller();
            mDeleteThemeInfos = new ArrayList<ThemeInfo>();
			
		}
		
		@Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
        {
            return new MyHodler(LayoutInflater.from(ThemePickActivity.this).inflate(R.layout.recycle_item, parent, false));
        }
		
		@Override
        public int getItemCount()
        {
            return themes.size();
        }
		
		@Override
		public void onBindViewHolder(RecyclerView.ViewHolder holder,
				final int position) {
			// Log.i("tyty","onBindViewHolder");
			final Object data = themes.get(position);
			if (data instanceof ThemeInfo) {
				((MyHodler) holder).mTextView.setText(((ThemeInfo) data)
						.getThemeName());
				Drawable drawable = ((ThemeInfo) data)
						.getThemeThumbnailsDrawable();
				if (drawable != null) {
					((MyHodler) holder).mImageView.setImageDrawable(drawable);
				} else {
					((MyHodler) holder).mImageView
							.setImageResource(((ThemeInfo) data)
									.getThemeThumbnails());
				}
				// check but
				final CheckBox checkBox = ((MyHodler) holder).mCheckBox;
				checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
					@Override
					public void onCheckedChanged(CompoundButton compoundButton,
							boolean b) {
						if (b) {
							((ThemeInfo) data)
									.setFlag(ThemeInfo.THEME_FLAG_DELETE);
							mDeleteThemeInfos.add(((ThemeInfo) data));
						} else {
							((ThemeInfo) data)
									.setFlag(ThemeInfo.THEME_FLAG_NORMAL);
							mDeleteThemeInfos.remove(data);
						}
					}
				});
				if (checkLongState
						&& (((ThemeInfo) data).getFlag() != ThemeInfo.THEME_FLAG_DEFAULT)) {
					checkBox.setVisibility(View.VISIBLE);
					// 不是当前主题的checkbox,才能选择
					if (mCurrentCheckBox != null
							&& mCurrentCheckBox == checkBox) {
						// don't doing anything
					} else {
						if (((ThemeInfo) data).getFlag() == ThemeInfo.THEME_FLAG_NORMAL) {
							checkBox.setChecked(false);
						} else if (((ThemeInfo) data).getFlag() == ThemeInfo.THEME_FLAG_DELETE) {
							checkBox.setChecked(true);
						}

					}

				} else {
					checkBox.setChecked(false);
					checkBox.setVisibility(View.GONE);

				}

				((MyHodler) holder).mImageView.setClickable(true);
				((MyHodler) holder).mImageView
						.setOnClickListener(new View.OnClickListener() {
							@Override
							public void onClick(View view) {

								if (checkLongState) {
									onBackPressed();
									return;
								}

								String packageName = ((ThemeInfo) data)
										.getPackageName();
								// if
								// (!mThemeInstaller.themeIsInstall(packageName,
								// pm))
								// {
								// mThemeInstaller.installTheme(packageName,
								// pm);
								// } else
								// {
								// //check theme
								// Toast.makeText(ThemePickActivity.this,
								// "theme is " + packageName,
								// Toast.LENGTH_SHORT).show();
								// }

								if (mCurrentCheckBox != null) {
									mCurrentCheckBox.setEnabled(true);
									mCurrentCheckBox.setVisibility(View.GONE);
								}
								// not check agait
								// checkBox.setChecked(true);
								{
									checkState = true;
									checkBox.setVisibility(View.VISIBLE);
									checkBox.setEnabled(false);
									mStateClearCallback = new IStateClearCallback() {
										@Override
										public void doClearWithHearts() {
											exitCheckState();
											mApplyTextView
													.setVisibility(View.GONE);
											checkBox.setEnabled(true);
											checkState = false;

										}
									};
									mApplyTextView.setText("Apply");
									mApplyTextView.setVisibility(View.VISIBLE);
									mApplyTextView
											.setOnClickListener(new View.OnClickListener() {
												@Override
												public void onClick(View v) {
													final String packageName = ((ThemeInfo) data)
															.getPackageName();
													// set current theme.

													// theme is apk? or has
													// install ?
													// only has install
													final ProgressDialog progressDialog = ProgressDialog
															.show(mContext,
																	"主题设置",
																	"主题设置中i。。。",
																	false);
													progressDialog.show();
													new Thread(new Runnable() {
														@Override
														public void run() {
															try {
																mThemeInstaller
																		.setTheme(
																				(ThemeInfo) data,
																				mContext);
																// Log.i("tyty","theme set finish");
																progressDialog
																		.dismiss();
																ThemeSharePerfence
																		.setThemeShareprefValue(
																				packageName,
																				mContext);

																// android.os.Process.killProcess(android.os.Process.myPid());
																restartLauncherActivity();
															} catch (IOException e) {
																e.printStackTrace();
															}
														}
													}).start();

													// if
													// restartLauncherActivity
													// fast, the pref will no
													// restore.
													// ThemeSharePerfence.setThemeShareprefValue(packageName,
													// mContext);

													// return
													onBackPressed();

												}
											});
								}
								mCurrentCheckBox = checkBox;
							}
						});
				((MyHodler) holder).mImageView.setLongClickable(true);
				((MyHodler) holder).mImageView
						.setOnLongClickListener(new View.OnLongClickListener() {
							@Override
							public boolean onLongClick(View view) {
								String packageName = ((ThemeInfo) data)
										.getPackageName();
								//

								// if
								// (mThemeInstaller.themeIsInstall(packageName,
								// pm))
								// {
								// mThemeInstaller.unInstallTheme(packageName,
								// pm, mContext);
								// } else
								// {
								// //remove file
								// Toast.makeText(ThemePickActivity.this,
								// "file remove " + packageName,
								// Toast.LENGTH_SHORT).show();
								// }
								// current checkbox must enable
								if (mCurrentCheckBox != null) {
									mCurrentCheckBox.setEnabled(true);
								}

								// if long press ,clear check state and show
								// text delete
								checkLongState = true;
								checkState = false;
								mStateClearCallback = new IStateClearCallback() {
									@Override
									public void doClearWithHearts() {
										// 去掉,checkbox的扎u那个状态
										ArrayList<ThemeInfo> mlist = getDeleteThemeInfos();
										for (int i = 0; i < mlist.size(); i++) {
											mlist.get(i)
													.setFlag(
															ThemeInfo.THEME_FLAG_NORMAL);
										}

										// 隐藏操作view
										mApplyTextView.setVisibility(View.GONE);
										// 长按状态取消
										checkLongState = false;
										// 更新list。。。。recycle
										notifyDataSetChanged();
									}
								};
								mApplyTextView.setText("Delete");
								mApplyTextView.setVisibility(View.VISIBLE);
								mApplyTextView
										.setOnClickListener(new View.OnClickListener() {
											@Override
											public void onClick(View view) {

												// set theme .and restart
												// activity.

												// delete theme .can get list
												ArrayList<ThemeInfo> lists = recycleViewAdapter
														.getDeleteThemeInfos();
												ArrayList<Object> aalsit = ThemeContainer
														.getInstance()
														.getAllThemeList(
																ThemePickActivity.this);

												for (ThemeInfo ThemeInfo : lists) {
													if (recycleViewAdapter
															.getThemeInstaller()
															.uninstallTheme(
																	ThemeInfo
																			.getPackageName(),
																	pm,
																	mContext)) {
														recycleViewAdapter
																.setThemes(aalsit);
														recycleViewAdapter
																.notifyDataSetChanged();
													}
												}
												onBackPressed();

											}
											// back

										});

								RecycleViewAdapter.this.notifyDataSetChanged();
								return true;
							}
						});

				// data deal
				if (mCurrentThemeName != null
						&& ((ThemeInfo) data).getPackageName().equals(
								mCurrentThemeName)) {
					checkBox.setEnabled(false);
					checkBox.setVisibility(View.VISIBLE);
					mCurrentCheckBox = checkBox;
					// imageview not working ?for click. < setClickable >
					((MyHodler) holder).mImageView.setEnabled(false);
					// ((MyHodler) holder).mImageView.setOnClickListener(null);
				}

			}

		}

		private void exitCheckState() {
			if (mCurrentCheckBox != null) {
				mCurrentCheckBox.setChecked(false);
				mCurrentCheckBox.setVisibility(View.GONE);
			}
		}

		private ArrayList<ThemeInfo> getDeleteThemeInfos() {
			return mDeleteThemeInfos;
		}

		private ThemeInstaller getThemeInstaller() {
			return mThemeInstaller;
		}
		
	}
	
	class MyHodler extends RecyclerView.ViewHolder
    {

        public ImageView mImageView;
        public TextView mTextView;
        public CheckBox mCheckBox;

        public MyHodler(View itemView)
        {
            super(itemView);
            mImageView = (ImageView) itemView.findViewById(R.id.recycler_view_item_icon);
            mTextView = (TextView) itemView.findViewById(R.id.recycler_view_item_name);
            mCheckBox = (CheckBox) itemView.findViewById(R.id.theme_item_check);


        }


    }
		
	
	interface IStateClearCallback
    {
        public void doClearWithHearts();
    }

}

其他逻辑类
添加ThemeSharePerfence.java

package com.android.launcher3.theme;

import android.content.Context;
import android.content.SharedPreferences;
/**
 * SharedPreferences封装类
 * @author zrx
 *
 */
public class ThemeSharePerfence {
	public static final String THEME_SHAREPREF_NAME = "theme";
	public static final String THEME_SHAREPREF_THEME_CURRENT_NAME = "theme_current";
	
	public static SharedPreferences getThemePreferences(Context context){
		return context.getSharedPreferences(THEME_SHAREPREF_NAME, Context.MODE_PRIVATE);
	}
	
	public static String getThemeShareprefValue(Context context){
		return getThemePreferences(context).getString(THEME_SHAREPREF_THEME_CURRENT_NAME, null);
	}
	
	public static boolean setThemeShareprefValue(String value, Context context){
		SharedPreferences.Editor editor = getThemePreferences(context).edit();
        editor.putString(THEME_SHAREPREF_THEME_CURRENT_NAME, value);
        return editor.commit();
	}

}

添加ThemeInfo.java,主题详细信息

package com.android.launcher3.theme;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import java.util.HashMap;
/**
 * theme info
 * @author zrx
 *
 */
public class ThemeInfo extends Object {

	public static final int BROWSER = 0;
	public static final int CALCULATOR = 1;
	public static final int CAMERA = 2;
	public static final int CONTACTS = 3;
	public static final int DESKCLOCK = 4;
	public static final int DOWNLOADS = 5;
	public static final int EMAIL = 6;
	public static final int FILEMANAGER = 7;
	public static final int FM = 8;
	public static final int GALLERY = 9;
	public static final int GOOGLE_CHROME = 10;
	public static final int GOOGLE_DOCS = 11;
	public static final int GOOGLE_GM = 12;
	public static final int GOOGLE_MAP = 13;
	public static final int GOOGLE_MUSIC = 14;
	public static final int GOOGLE_PHOTOS = 15;
	public static final int GOOGLEQUICKSEARCHBOX = 16;
	public static final int GOOGLE_TACHYON = 17;
	public static final int GOOGLE_VIDEOS = 18;
	public static final int MSG = 19;
	public static final int MUSIC = 20;
	public static final int PHONE = 21;
	public static final int RADIO = 22;
	public static final int SAMWEATHERCLOCK = 23;
	public static final int SETTING = 24;
	public static final int SIM_CARD = 25;
	public static final int SOUNDRECORDER = 26;
	public static final int YOUTUBE = 27;
	public static final int CALENDAR = 28;
	public static final int GOOGLEPLAY = 29;

	public static final String[] COMSTOM_ICONS = new String[] {
			// .browser - > browser
			"browser",
			// .calculator - > calculator
			"calculator",
			// .camera - > camera
			"camera",
			// .contacts - > contacts
			"contacts",
			// .deskclock - > deskclock
			"deskclock",
			// .downloads - > downloads
			"downloads",
			// .email - > email
			"email",
			// .filemanager - > filemanager
			"filemanager",
			// .fm - > fm
			"fm",
			// .gallery - > gallery
			"gallery",
			// .google_chrome - > google_chrome
			"google_chrome",
			// .google_docs - > google_docs
			"google_docs",
			// .google_gm - > google_gm
			"google_gm",
			// .google_map - > google_map
			"google_map",
			// .google_music - > google_music
			"google_music",
			// .google_photos - > google_photos
			"google_photos",
			// .googlequicksearchbox - > googlequicksearchbox
			"googlequicksearchbox",
			// .google_tachyon - > google_tachyon
			"google_tachyon",
			// .google_videos - > google_videos
			"google_videos",
			// .msg - > msg
			"msg",
			// .music - > music
			"music",
			// .phone - > phone
			"phone",
			// .radio - > radio
			"radio",
			// .samweatherclock - > samweatherclock
			"samweatherclock",
			// .setting - > setting
			"setting",
			// .sim_card - > sim_card
			"sim_card",
			// .soundrecorder - > soundrecorder
			"soundrecorder",
			// .youtube - > youtube
			"youtube",
			// .calendar - > calendar
			"calendar",
			// .googleplay - > google_play
			"google_play" };
	
	public static HashMap<String, String> THEME_ICON_CONSTANT = new HashMap<String, String>();
	static{//静态块
		    THEME_ICON_CONSTANT.put("com.android.browser", COMSTOM_ICONS[BROWSER]);
	        THEME_ICON_CONSTANT.put("com.android.calculator2", COMSTOM_ICONS[CALCULATOR]);
	        THEME_ICON_CONSTANT.put("com.mediatek.camera", COMSTOM_ICONS[CAMERA]);
	        THEME_ICON_CONSTANT.put("com.android.contacts", COMSTOM_ICONS[CONTACTS]);
	        THEME_ICON_CONSTANT.put("com.android.deskclock", COMSTOM_ICONS[DESKCLOCK]);

	        THEME_ICON_CONSTANT.put("com.android.documentsui", COMSTOM_ICONS[DOWNLOADS]);
	        THEME_ICON_CONSTANT.put("com.android.email", COMSTOM_ICONS[EMAIL]);
	        THEME_ICON_CONSTANT.put("com.mediatek.filemanager", COMSTOM_ICONS[FILEMANAGER]);
	        THEME_ICON_CONSTANT.put("com.android.fmradio", COMSTOM_ICONS[FM]);
	        THEME_ICON_CONSTANT.put("com.android.gallery3d", COMSTOM_ICONS[GALLERY]);

	        THEME_ICON_CONSTANT.put("com.android.chrome", COMSTOM_ICONS[GOOGLE_CHROME]);
	        THEME_ICON_CONSTANT.put("com.google.android.apps.docs", COMSTOM_ICONS[GOOGLE_DOCS]);
	        THEME_ICON_CONSTANT.put("com.google.android.gm", COMSTOM_ICONS[GOOGLE_GM]);
	        THEME_ICON_CONSTANT.put("com.google.android.apps.maps", COMSTOM_ICONS[GOOGLE_MAP]);
	        THEME_ICON_CONSTANT.put("com.google.android.music", COMSTOM_ICONS[GOOGLE_MUSIC]);

	        THEME_ICON_CONSTANT.put("com.google.android.apps.photos", COMSTOM_ICONS[GOOGLE_PHOTOS]);
	        THEME_ICON_CONSTANT.put("com.google.android.googlequicksearchbox", COMSTOM_ICONS[GOOGLEQUICKSEARCHBOX]);
	        THEME_ICON_CONSTANT.put("com.google.android.apps.tachyon", COMSTOM_ICONS[GOOGLE_TACHYON]);
	        THEME_ICON_CONSTANT.put("com.google.android.videos", COMSTOM_ICONS[GOOGLE_VIDEOS]);
	        THEME_ICON_CONSTANT.put("com.android.mms", COMSTOM_ICONS[MSG]);

	        THEME_ICON_CONSTANT.put("com.android.music", COMSTOM_ICONS[MUSIC]);
	        THEME_ICON_CONSTANT.put("com.android.dialer", COMSTOM_ICONS[PHONE]);
	        THEME_ICON_CONSTANT.put("com.android.soundrecorder", COMSTOM_ICONS[SAMWEATHERCLOCK]);
	        THEME_ICON_CONSTANT.put("com.android.settings", COMSTOM_ICONS[SETTING]);

	        THEME_ICON_CONSTANT.put("com.android.stk", COMSTOM_ICONS[SIM_CARD]);
	        THEME_ICON_CONSTANT.put("com.android.soundrecorder", COMSTOM_ICONS[SOUNDRECORDER]);
	        THEME_ICON_CONSTANT.put("com.google.android.youtube", COMSTOM_ICONS[YOUTUBE]);
		    THEME_ICON_CONSTANT.put("com.android.calendar", COMSTOM_ICONS[CALENDAR]);
	        THEME_ICON_CONSTANT.put("com.android.vending", COMSTOM_ICONS[GOOGLEPLAY]);
	}
	
	public static final boolean HAS_CUSTOM_SHORTICON = false;
	private HashMap<String, Bitmap> mCustomShorticon = new HashMap<String, Bitmap>();

	private static final String DRAWABLE = "drawable";
	private static final String STRING = "string";
	private static final String COLOR = "color";
	private static final String DIMEN = "dimen";

	private static final String THEME_NAME = "theme_name";
	private static final String THEME_THUMB = "theme_thumb";
	private static final String THEME_ROUND = "theme_round";
	private static final String THEME_COLOR = "theme_shortcut_backgrand_color";
	private static final String THEME_WALLPAPER = "theme_wallpaper";
	
	// 添加两个状态,normal,delete,default-cant delete
    public static final int THEME_FLAG_DEFAULT = 0;
    public static final int THEME_FLAG_NORMAL = 1;
    public static final int THEME_FLAG_DELETE = 2;
       
    //name
    private String mThemeName;

    //and else .
    //package name
    private String mPackageName;
    //theme resources
    private Resources mResources;
    //thumbnail
    private int mThemeThumbnails;
    //thumbnail
    private Drawable mThemeThumbnailsDrawable;

    //launcher true resouce. wallpaper
    private int mThemeWallPaper;

    //parameter in FastBitmapDrawable
    //FastBitmapDrawable backgrand
    private Drawable mThemeShortcutBackgrand;
    //FastBitmapDrawable backgrand
    private int mThemeShortcutBackgrandColor;
    //Round angle
    private float mThemeRound;

    private int flag;
    
    public ThemeInfo(String themeName, int themeThumbnails)
    {
        mThemeName = themeName;
        mThemeThumbnails = themeThumbnails;
        flag = THEME_FLAG_NORMAL;
    }
    
    public ThemeInfo(Drawable themeThumbnailsDrawable, String themeName)
    {
        mThemeThumbnailsDrawable = themeThumbnailsDrawable;
        mThemeName = themeName;
        flag = THEME_FLAG_NORMAL;
    }

    public ThemeInfo(String packageName, Resources resources)
    {
        mPackageName = packageName;
        mResources = resources;
        flag = THEME_FLAG_NORMAL;
        //if get a resources,we need read again
        readTheResources();
    }
    public String getThemeName()
    {
        return mThemeName;
    }

    public void setThemeName(String themeName)
    {
        mThemeName = themeName;
    }

    public int getThemeThumbnails()
    {
        return mThemeThumbnails;
    }

    public void setThemeThumbnails(int themeThumbnails)
    {
        mThemeThumbnails = themeThumbnails;
    }

    public String getPackageName()
    {
        return mPackageName;
    }

    public void setPackageName(String packageName)
    {
        mPackageName = packageName;
    }


    public Drawable getThemeThumbnailsDrawable()
    {
        return mThemeThumbnailsDrawable;
    }

    public void setThemeThumbnailsDrawable(Drawable themeThumbnailsDrawable)
    {
        mThemeThumbnailsDrawable = themeThumbnailsDrawable;
    }

	public Bitmap getThemeWallPaper() {
		if (mResources == null) {
			return null;
		}
		return ((BitmapDrawable) mResources.getDrawable(mResources
				.getIdentifier(THEME_WALLPAPER, DRAWABLE, mPackageName)))
				.getBitmap();
	}



    public Drawable getThemeShortcutBackgrand()
    {
        return mThemeShortcutBackgrand;
    }

    public void setThemeShortcutBackgrand(Drawable themeShortcutBackgrand)
    {
        mThemeShortcutBackgrand = themeShortcutBackgrand;
    }

    public int getThemeShortcutBackgrandColor()
    {
        return mThemeShortcutBackgrandColor;
    }

    public void setThemeShortcutBackgrandColor(int themeShortcutBackgrandColor)
    {
        mThemeShortcutBackgrandColor = themeShortcutBackgrandColor;
    }

    public float getThemeRound()
    {
        return mThemeRound;
    }

    public void setThemeRound(float themeRound)
    {
        mThemeRound = themeRound;
    }

    public void setCurrentTheme(Context context)
    {
        // set wallpaper


        //set launcher Icon
        //set shareperfence

    }

    public HashMap<String, Bitmap> getCustomShorticon()
    {
        return mCustomShorticon;
    }


    public int getFlag()
    {
        return flag;
    }

    public void setFlag(int flag)
    {
        this.flag = flag;
    }
    
    private void readTheResources(){
    	if (mResources != null){
    		mThemeRound = mResources.getDimension(mResources.getIdentifier(THEME_ROUND, DIMEN, mPackageName));
    		mThemeShortcutBackgrandColor = mResources.getColor(mResources.getIdentifier(THEME_COLOR, COLOR, mPackageName));
    		mThemeThumbnailsDrawable = mResources.getDrawable(mResources.getIdentifier(THEME_THUMB, DRAWABLE, mPackageName));
    		mThemeName = mResources.getString(mResources.getIdentifier(THEME_NAME, STRING, mPackageName));
    		mThemeWallPaper = mResources.getIdentifier(THEME_WALLPAPER, DRAWABLE, mPackageName);
    		if(HAS_CUSTOM_SHORTICON){
    			for (String comstomIcon : COMSTOM_ICONS)
                {
                    Bitmap bitmap = ((BitmapDrawable)mResources.getDrawable(mResources.getIdentifier(comstomIcon, DRAWABLE, mPackageName))).getBitmap();
                    mCustomShorticon.put(comstomIcon,bitmap);
                }
    		}
    		
    	}
    }
    
    
    
    @Override
    public String toString()
    {
        return "mThemeName = " + mThemeName + " - mPackageName =" + mPackageName;
    }


}

添加GridSpacingItemDecoration.java,RecyclerView属性的封装类

package com.android.launcher3.theme;

import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration
{
    private int spanCount;//列数
    private int spacing;//间隔
    private boolean includeEdge;//是否包含边缘

    public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge)
    {
        this.spanCount = spanCount;
        this.spacing = spacing;
        this.includeEdge = includeEdge;
    }

    /**
     * getItemOffsets 中为 outRect 设置的4个方向的值,将被计算进所有 decoration 的尺寸中,而这个尺寸,被计入了 RecyclerView 每个 item view 的 padding 中
     *
     * @param outRect
     * @param view
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
    {
        int position = parent.getChildAdapterPosition(view); // item position
        int column = position % spanCount; // item column
        //Log.i("tyty", "l = " + outRect.left + " r = " + outRect.right + "  t = " + outRect.top + "  b = " + outRect.bottom + " position = " + position);
        //此处 巧妙的 通过 计算,。。。,然 相邻的两个view之间 左右 之和 =  50 ,然后 保证 最右边等于五十,最左边也等于五十。
        //特么的  其实 就让最边上的 固定,然后中间的均分 就好了! 不过。 这样会有档次些嘛 -0-!

        if (includeEdge)
        {
            outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
            outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)

            if (position < spanCount)
            { // top edge
                outRect.top = spacing;
            }
            outRect.bottom = spacing; // item bottom
        } else
        {
            outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
            outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f /    spanCount) * spacing)
            if (position >= spanCount)
            {
                outRect.top = spacing; // item top
            }
        }

//        //clear rect
//        outRect.left = 0;
//        outRect.right = 0;
//        outRect.top = 0;
//        outRect.bottom = 0;
//        // reset rect
//        outRect.left = 0;


      //  Log.i("tyty", "new one " + "l = " + outRect.left + " r = " + outRect.right + "  t = " + outRect.top + "  b = " + outRect.bottom);


    }
}

添加ThemeContainer.java,获取主题列表信息

package com.android.launcher3.theme;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;

import com.android.launcher3.R;
/**
 * theme list
 * @author zrx
 *
 */
public class ThemeContainer {
	private static final String THEME_INTENT_ACTION = "com.zrx.theme";
	private ArrayList<Object> datas;
	private static ThemeContainer mContainer;
	private static final String TAG = "ThemeContainer";
	
	public static ThemeContainer getInstance(){
		if(mContainer == null){
			mContainer = new ThemeContainer();
		}
		return mContainer;
	}
	
	public ArrayList<Object> getAllThemeList(Context context){
		datas = queryTheme(context);
		if(datas == null){
			return createFakeData();
		}
		return datas;		
	}
	
	 private ArrayList<Object> createFakeData()
	    {
	        ArrayList<Object> temp = new ArrayList<Object>();
	        for (int i = 0; i < 20; i++)
	        {
	        	ThemeInfo themeZhaH = new ThemeInfo("item" + (i + 1), R.drawable.music);
	            themeZhaH.setPackageName("packageName" + i);
	            temp.add(themeZhaH);
	        }
	        return temp;
	    }
	
	
	private ArrayList<Object> queryTheme(Context context){
		ArrayList<Object> items = new ArrayList<Object>();
		PackageManager pm = context.getPackageManager();
		
		Intent intent = new Intent();
		intent.setAction(THEME_INTENT_ACTION);
		
		List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
		for(ResolveInfo reInfo:resolveInfos){
			ActivityInfo activityInfo = reInfo.activityInfo;
			/*Bundle metaData = activityInfo.metaData;
			String name = activityInfo.name;*/
			try {
				Context remoteContext = context.createPackageContext(activityInfo.packageName, Context.CONTEXT_IGNORE_SECURITY);//获得package的context
				Resources resources = remoteContext.getResources();
				ThemeInfo themeInfo = new ThemeInfo(activityInfo.packageName, resources);//获取第三方主题app的资源信息
				items.add(themeInfo);
			} catch (Exception e) {
				// TODO: handle exception
				e.printStackTrace();
			}
			
		}
		return items;
	}

}

添加ThemeInstaller.java,设置主题后壁纸处理类

package com.android.launcher3.theme;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;

import android.app.WallpaperManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Environment;

import com.android.launcher3.R;

/**
 * 
 * @author zrx
 *
 */
public class ThemeInstaller implements IThemeInstallImp{

	@Override
	public void installTheme(String packageName, PackageManager pm) {
		// TODO Auto-generated method stub
		//install theme
        Uri mPackageURI = Uri.fromFile(new File(Environment.getExternalStorageDirectory() + packageName));

        int installFlags = 0;
/*
        try
        {
            PackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
            if (pi != null)
            {
                installFlags |= PackageManager.REPLACE_EXISTING_PACKAGE;
            }

        } catch (PackageManager.NameNotFoundException e)
        {
        }
        PackageInstallObserver observer = new PackageInstallObserver();
        pm.installPackage(mPackageURI, observer, installFlags);
    */

        //delete apk file ,if install.
	}

	@Override
	public boolean uninstallTheme(String packageName, PackageManager pm,
			Context context) {
		// TODO Auto-generated method stub
		 //need system id
        //pm.deletePackage(packageName, null, 0);

/*
        // uninstall
        String appPackage = packageName;
        Intent intent = new Intent(mContext, mContext.getClass());
        PendingIntent sender = PendingIntent.getActivity(mContext, 0, intent, 0);
        PackageInstaller mPackageInstaller = mContext.getPackageManager().getPackageInstaller();
        mPackageInstaller.uninstall(appPackage, sender.getIntentSender());
*/
	//PackageInstaller packageInstaller = pm.getPackageInstaller();

 /* reflection need permission.
        Class<?> activityTherad;
        Method method;
        Class pmService = null;
        try
        {
            activityTherad = Class.forName("android.app.ActivityThread");


            Class<?> paramTypes[] = getParamTypes(activityTherad, "getPackageManager");

            method = activityTherad.getMethod("getPackageManager", paramTypes);

            Object PackageManagerService = null;

            PackageManagerService = method.invoke(activityTherad);


            pmService = PackageManagerService.getClass();

            Class<?> paramTypes1[] = getParamTypes(pmService, "deletePackage");
            method = pmService.getMethod("deletePackage", paramTypes1);
            method.invoke(PackageManagerService, packageName, null,PackageManager.DELETE_ALL_USERS, 0);


        } catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        } catch (NoSuchMethodException e)
        {
            e.printStackTrace();
        } catch (IllegalAccessException e)
        {
            e.printStackTrace();
        } catch (InvocationTargetException e)
        {
            e.printStackTrace();
        }

*/


       Intent intent = new Intent(
                    Intent.ACTION_DELETE, Uri.fromParts("package", packageName, "MainActivity"));
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                    Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        context.startActivity(intent);

//test true
        return true;
	}

	@Override
	public boolean themeIsInstall(String packageName, PackageManager pm) {
		// TODO Auto-generated method stub
		boolean installed = false;
        try
        {
            pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
            installed = true;
        } catch (PackageManager.NameNotFoundException e)
        {
            installed = false;
        }
        return installed;
	}

	@Override
	public void setTheme(ThemeInfo themeZhaH, Context context)
			throws IOException {
		// TODO Auto-generated method stub
		 //wallpaper
        WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		Bitmap bitmap = null;
		bitmap = themeZhaH.getThemeWallPaper();
		if (bitmap == null) {
			bitmap = ((BitmapDrawable) context.getResources().getDrawable(
					R.drawable.default_wallpaper)).getBitmap();
		}
		bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
				byteArrayOutputStream.toByteArray());
		wallpaperManager.setStream(byteArrayInputStream, null, true,
				WallpaperManager.FLAG_SYSTEM);
		byteArrayInputStream.close();
		byteArrayOutputStream.close();


        //theme_shortcut_backgrand

        //icon?
	}
	
	/***
     * 获取参数类型,返回值保存在Class[]中
     */
    private Class<?>[] getParamTypes(Class<?> cls, String mName)
    {
        Class<?>[] clazz = null;
        /**Note: 由于我们一般通过反射机制调用的方法,是非public方法
         * 所以在此处使用了getDeclaredMethods()方法*/
        Method[] mtd = cls.getDeclaredMethods();
        for (int i = 0; i < mtd.length; i++)
        {
            if (!mtd[i].getName().equals(mName))
            {
                // 不是我们需要的方法,则进入下一次循环
                continue;
            }
            clazz = mtd[i].getParameterTypes();
        }
        return clazz;
    }

}

interface IThemeInstallImp
{
    public void installTheme(String packageName, PackageManager pm);

    public boolean uninstallTheme(String packageName, PackageManager pm,Context context);

    public boolean themeIsInstall(String packageName, PackageManager pm);

    public void setTheme(ThemeInfo themeZhaH, Context context) throws IOException;


}

添加ThemeBitmap.java,修改主题后获取app的icon

package com.android.launcher3.theme;

import java.util.HashMap;

import com.android.launcher3.LauncherAppState;

import android.R.drawable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.LauncherActivityInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
public class ThemeBitmap {
	private Context mContext;
	private PackageManager pm;
	private LauncherActivityInfo mLauncherActivityInfo;
	
	public ThemeBitmap(LauncherActivityInfo launcherActivityInfo, Context context){
		mLauncherActivityInfo = launcherActivityInfo;
		mContext = context;
		pm = mContext.getPackageManager();
	}
	
	private String getCurrenThemePKGName(Context context)
    {
        return ThemeSharePerfence.getThemeShareprefValue(context);
    }

	private void clearCurrentThemeData(Context context)
    {
        ThemeSharePerfence.setThemeShareprefValue(null, context);
    }
	
	public Bitmap getBitmap(){
		Bitmap bitmap = null;
		Resources resources = null;
		String pacakgeName = getCurrenThemePKGName(mContext);
		if(pacakgeName != null){
			try {
				Context remoteContext = mContext.createPackageContext(pacakgeName, Context.CONTEXT_IGNORE_SECURITY);
				resources = remoteContext.getResources();
			} catch (PackageManager.NameNotFoundException e) {
				// TODO: handle exception
				e.printStackTrace();
				clearCurrentThemeData(mContext);
				return null;
			}
		}
		HashMap<String, String> appNames = ThemeInfo.THEME_ICON_CONSTANT;
		if(appNames.containsKey(mLauncherActivityInfo.getComponentName().getPackageName())){
			Drawable drawable = null;
			if(resources != null){
				drawable = resources.getDrawable(resources.getIdentifier(ThemeInfo.THEME_ICON_CONSTANT.get(mLauncherActivityInfo.getComponentName().getPackageName()), "drawable", pacakgeName), null);		
			}
			if(drawable != null){
				return makeDefaultIcon(drawable, -1, 1.0f);
			}
			return null;
			
		}
		if(resources != null){
			int color = resources.getColor(resources.getIdentifier("theme_shortcut_backgrand_color", "color", pacakgeName));
			float round = resources.getDimension(resources.getIdentifier("theme_round", "dimen", pacakgeName))/(100*2);
			return makeDefaultIcon(mLauncherActivityInfo.getApplicationInfo().loadIcon(pm), color, round);
		}
		return null;
		
	}
	
    private Bitmap makeDefaultIcon(Drawable d, int color, float scalf)
    {

        Bitmap bitmap = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1), Math.max(d.getIntrinsicHeight(), 1), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);
        final float roundPx = 24;

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        canvas.save();

        Matrix matrix = new Matrix();
        matrix.setScale(scalf, scalf);
        float margin = (1.0f - scalf) / 2;
	//Log.i("tyty","margin = "+margin);
        matrix.preTranslate(bitmap.getWidth() * margin, bitmap.getHeight() * margin);
        canvas.setMatrix(matrix);
        paint.setColor(color);

        if (color != -1)
        {
            canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
        }

        canvas.restore();


        d.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
        d.draw(canvas);
        canvas.setBitmap(null);
        return bitmap;
    }
		
}

laucher图标处理,修改Launcher3/src/com/android/launcher3/IconCache.java

import com.android.launcher3.theme.ThemeBitmap;//zrx add

private synchronized void getTitleAndIcon(
            @NonNull ItemInfoWithIcon infoInOut,
            @NonNull Provider<LauncherActivityInfo> activityInfoProvider,
            boolean usePkgIcon, boolean useLowResIcon) {    	
        CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), activityInfoProvider,
                infoInOut.user, usePkgIcon, useLowResIcon);
          //zrx add start
        ThemeBitmap themeBitmap = new ThemeBitmap(activityInfoProvider.get(), mContext);
        Bitmap bitmap = themeBitmap.getBitmap();
        if(bitmap != null){
        	entry.icon = bitmap;
        }
        //zrx add end
        applyCacheEntry(entry, infoInOut);
    }

三:theme控件点击事件
修改Launcher.java

import com.android.launcher3.theme.ThemePickActivity;

private void startThemePickActivity()
	{
		Intent intent = new Intent(this,ThemePickActivity.class);
		startActivity(intent);
		
	}
private void setupOverviewPanel() {
        mOverviewPanel = (ViewGroup) findViewById(R.id.overview_panel);
        //zrx add start
        View themeButton = findViewById(R.id.theme_button);
        new OverviewButtonClickListener(ControlType.THEME_BUTTON) {
            @Override
            public void handleViewClick(View view) {
            	startThemePickActivity();
            }
        }.attachTo(themeButton);
        //zrx add end
        // Bind wallpaper button actions
        View wallpaperButton = findViewById(R.id.wallpaper_button);
        new OverviewButtonClickListener(ControlType.WALLPAPER_BUTTON) {
            @Override
            public void handleViewClick(View view) {
                onClickWallpaperPicker(view);
            }
        }.attachTo(wallpaperButton);

        // Bind widget button actions
        mWidgetsButton = findViewById(R.id.widget_button);
        new OverviewButtonClickListener(ControlType.WIDGETS_BUTTON) {
            @Override
            public void handleViewClick(View view) {
                onClickAddWidgetButton(view);
            }
        }.attachTo(mWidgetsButton);

        // Bind settings actions
        View settingsButton = findViewById(R.id.settings_button);
        boolean hasSettings = hasSettings();
        if (hasSettings) {
            new OverviewButtonClickListener(ControlType.SETTINGS_BUTTON) {
                @Override
                public void handleViewClick(View view) {
                    onClickSettingsButton(view);
                }
            }.attachTo(settingsButton);
        } else {
            settingsButton.setVisibility(View.GONE);
        }

        mOverviewPanel.setAlpha(0f);
    }

修改Launcher3/protos/launcher_log.proto
enum ControlType {
DEFAULT_CONTROLTYPE = 0;
ALL_APPS_BUTTON = 1;
WIDGETS_BUTTON = 2;
WALLPAPER_BUTTON = 3;
SETTINGS_BUTTON = 4;
REMOVE_TARGET = 5;
UNINSTALL_TARGET = 6;
APPINFO_TARGET = 7;
RESIZE_HANDLE = 8;
VERTICAL_SCROLL = 9;
HOME_INTENT = 10; // Deprecated, use enum Command instead
BACK_BUTTON = 11; // Deprecated, use enum Command instead
// GO_TO_PLAYSTORE
THEME_BUTTON = 12; //zrx add
}

四:AndroidManifest.xml修改,声明ThemePickActivity和权限添加

    <!--add by zrx  theme-->
    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />  
    <uses-permission android:name="android.permission.DELETE_PACKAGES" />  
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>  
    <!-- add end-->
    <activity
 	        android:label="@string/theme_title"
            android:name="com.android.launcher3.theme.ThemePickActivity"
            android:screenOrientation="portrait"
            >
   </activity>

五:主题apk创建,在AndroidManifest.xml的Activity元素下添加

 <intent-filter>
            <action android:name="com.zrx.theme"/>
 </intent-filter>

把资源文件放到res目录下,资源命令规则查看ThemeInfo.java

五:运行效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值