标签:
/** * Event handler for the wallpaper picker button that appears after a long press * on the home screen. */ protected void onClickWallpaperPicker(View v) { if (LOGD) Log.d(TAG, "onClickWallpaperPicker"); final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER); pickWallpaper.setComponent(getWallpaperPickerComponent()); startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER); if (mLauncherCallbacks != null) { mLauncherCallbacks.onClickWallpaperPicker(v); } }
protected ComponentName getWallpaperPickerComponent() { if (mLauncherCallbacks != null) { return mLauncherCallbacks.getWallpaperPickerComponent(); } return new ComponentName(getPackageName(), LauncherWallpaperPickerActivity.class.getName()); }很显然,LauncherWallpaperPickerActivity就是壁纸设置界面了,LauncherWallpaperPickerActivity继承于WallpaperPickerActivity,WallpaperPickerActivity又继承于WallpaperCropActivity,这么多继承,看来这个界面还是比较复杂的。从命名来看的话,也是为了区分每个类的处理重点,WallpaperCropActivity用来进行壁纸的裁剪,将图片裁剪到合适的尺寸;WallpaperPickerActivity就是壁纸选择器,选择壁纸并设置;至于LauncherWallpaperPickerActivity,从代码中看到只是重写了父类的两个方法,没什么可分析的,这里我们重点分析WallpaperPickerActivity这个类。
public static abstract class WallpaperTileInfo { protected View mView; public Drawable mThumb; public void setView(View v) { mView = v; } public void onClick(WallpaperPickerActivity a) {}// 缩略图点击事件 public void onSave(WallpaperPickerActivity a) {}// 设置壁纸,并做一些保存操作 public void onDelete(WallpaperPickerActivity a) {}// 删除壁纸 public boolean isSelectable() { return false; }// 是否可选 public boolean isNamelessWallpaper() { return false; }// 壁纸是否没有名字 public void onIndexUpdated(CharSequence label) {// 更新索引 if (isNamelessWallpaper()) { mView.setContentDescription(label); } } }壁纸对象的一个抽象类,不直接使用,具体的壁纸继承该类并根据自身特点扩展。壁纸来源有多个途径,如应用内置的壁纸、图库、第三方等,另外设为壁纸的方式也不一定相同,需要对不同来源区分处理,所有就定义了以下几个壁纸类对象,
PickImageInfo--图片选择器,在Activity中添加属性<action android:name="android.intent.action.GET_CONTENT" />,就可以隐式调用到,如图库
UriWallpaperInfo--通过图片的Uri来设置壁纸
FileWallpaperInfo--通过图片文件来设置壁纸
ResourceWallpaperInfo--Launcher3中内置的壁纸资源来设置
DefaultWallpaperInfo--系统默认壁纸,资源在framework中这几个类实现其抽象父类中的方法,具体代码实现就不一一细说,后面说到具体方法时会举其中的例子来说明,这里对几个抽象方法已经做了注释。
<?xml version="1.0" encoding="utf-8"?><!-- /* ** ** Copyright 2013, 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. */ --> <com.android.launcher3.WallpaperRootView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/wallpaper_root" android:layout_width="match_parent" android:layout_height="match_parent"> <com.android.launcher3.CropView android:id="@+id/cropView" android:layout_width="match_parent" android:layout_height="match_parent" /> <ProgressBar android:id="@+id/loading" style="@android:style/Widget.Holo.ProgressBar.Large" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:background="@android:color/transparent" android:indeterminate="true" android:indeterminateOnly="true" android:visibility="invisible" /> <LinearLayout android:id="@+id/wallpaper_strip" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:orientation="vertical"> <View android:layout_width="match_parent" android:layout_height="2dp" android:background="@drawable/tile_shadow_top" /> <HorizontalScrollView android:id="@+id/wallpaper_scroll_container" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:id="@+id/master_wallpaper_list" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <LinearLayout android:id="@+id/wallpaper_list" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" /> <LinearLayout android:id="@+id/live_wallpaper_list" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" /> <LinearLayout android:id="@+id/third_party_wallpaper_list" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" /> </LinearLayout> </HorizontalScrollView> <View android:layout_width="match_parent" android:layout_height="2dp" android:background="@drawable/tile_shadow_bottom" /> </LinearLayout> </com.android.launcher3.WallpaperRootView>
WallpaperRootView是根视图,继承RelativeLayout自定义的一个视图,重写了fitSystemWindows方法,
protected boolean fitSystemWindows(Rect insets) { a.setWallpaperStripYOffset(insets.bottom); return true; }这么做的目的是为了让视图内离底部一段距离,否则会出现如图2的情况,跟虚拟键重合,就不大美观了。
setContentView(R.layout.wallpaper_picker); mCropView = (CropView) findViewById(R.id.cropView); mCropView.setVisibility(View.INVISIBLE);// 默认是不可见的 mWallpaperStrip = findViewById(R.id.wallpaper_strip);
2)进度条--加载该界面时的进度条。
3)壁纸列表--LinearLayout布局块,其中的子视图HorizontalScrollView是一个横向的滑动视图,就是我们的壁纸列表,也是根据壁纸类型的分了多个布局块,分别加载。
好像还少了ActionBar,这个是在代码中动态添加的,下面会说到。
mCropView.setTouchCallback(new CropView.TouchCallback() { ViewPropertyAnimator mAnim; @Override public void onTouchDown() { if (mAnim != null) { mAnim.cancel(); } if (mWallpaperStrip.getAlpha() == 1f) { mIgnoreNextTap = true; } mAnim = mWallpaperStrip.animate(); mAnim.alpha(0f) .setDuration(150) .withEndAction(new Runnable() { public void run() { mWallpaperStrip.setVisibility(View.INVISIBLE); } }); mAnim.setInterpolator(new AccelerateInterpolator(0.75f)); mAnim.start(); } @Override public void onTouchUp() { mIgnoreNextTap = false; } @Override public void onTap() { boolean ignoreTap = mIgnoreNextTap; mIgnoreNextTap = false; if (!ignoreTap) { if (mAnim != null) { mAnim.cancel(); } mWallpaperStrip.setVisibility(View.VISIBLE); mAnim = mWallpaperStrip.animate(); mAnim.alpha(1f) .setDuration(150) .setInterpolator(new DecelerateInterpolator(0.75f)); mAnim.start(); } } });
CropView的touch回调处理,这里只做了一些动画效果,具体裁剪的操作还是在CropView中实现的,这里就不详细说明了。
mThumbnailOnClickListener = new OnClickListener() { public void onClick(View v) { if (mActionMode != null) { // When CAB is up, clicking toggles the item instead if (v.isLongClickable()) { mLongClickListener.onLongClick(v); } return; } mSetWallpaperButton.setEnabled(true); WallpaperTileInfo info = (WallpaperTileInfo) v.getTag(); if (info.isSelectable() && v.getVisibility() == View.VISIBLE) { selectTile(v); } info.onClick(WallpaperPickerActivity.this);// 缩略图点击事件 } };
缩略图点击事件,如果处于ActionMode(长按事件),处理长按事件,否则回调该壁纸所实现的onClick方法,启用mSetWallpaperButton,该控件定义在其父类WallpaperCropActivity中,// Action bar // Show the custom action bar view final ActionBar actionBar = getActionBar(); actionBar.setCustomView(R.layout.actionbar_set_wallpaper); actionBar.getCustomView().setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { boolean finishActivityWhenDone = true; cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone); } }); mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
mLongClickListener = new View.OnLongClickListener() { // Called when the user long-clicks on someView public boolean onLongClick(View view) { CheckableFrameLayout c = (CheckableFrameLayout) view; c.toggle(); if (mActionMode != null) { mActionMode.invalidate(); } else { // Start the CAB using the ActionMode.Callback defined below mActionMode = startActionMode(mActionModeCallback); int childCount = mWallpapersView.getChildCount(); for (int i = 0; i < childCount; i++) { mWallpapersView.getChildAt(i).setSelected(false); } } return true; } };
定义了缩略图长按事件,并不是所有的壁纸类型都设置了长按事件,下面会讲到。
// Populate the built-in wallpapers // 填充内置壁纸,资源文件配置的壁纸和系统默认壁纸 ArrayList<WallpaperTileInfo> wallpapers = findBundledWallpapers(); mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list); SimpleWallpapersAdapter ia = new SimpleWallpapersAdapter(this, wallpapers); populateWallpapersFromAdapter(mWallpapersView, ia, false);通过findBundledWallpapers来查找壁纸,
private ArrayList<WallpaperTileInfo> findBundledWallpapers() { final PackageManager pm = getPackageManager(); final ArrayList<WallpaperTileInfo> bundled = new ArrayList<WallpaperTileInfo>(24); Partner partner = Partner.get(pm); if (partner != null) { final Resources partnerRes = partner.getResources(); final int resId = partnerRes.getIdentifier(Partner.RES_WALLPAPERS, "array", partner.getPackageName()); if (resId != 0) { addWallpapers(bundled, partnerRes, partner.getPackageName(), resId); } // Add system wallpapers File systemDir = partner.getWallpaperDirectory(); if (systemDir != null && systemDir.isDirectory()) { for (File file : systemDir.listFiles()) { if (!file.isFile()) { continue; } String name = file.getName(); int dotPos = name.lastIndexOf('.'); String extension = ""; if (dotPos >= -1) { extension = name.substring(dotPos); name = name.substring(0, dotPos); } if (name.endsWith("_small")) { // it is a thumbnail continue; } File thumbnail = new File(systemDir, name + "_small" + extension); Bitmap thumb = BitmapFactory.decodeFile(thumbnail.getAbsolutePath()); if (thumb != null) { bundled.add(new FileWallpaperInfo(file, new BitmapDrawable(thumb))); } } } } // 添加Launcher中配置的壁纸 Pair<ApplicationInfo, Integer> r = getWallpaperArrayResourceId(); if (r != null) { try { Resources wallpaperRes = getPackageManager().getResourcesForApplication(r.first); addWallpapers(bundled, wallpaperRes, r.first.packageName, r.second); } catch (PackageManager.NameNotFoundException e) { } } // 创建一个空的实体,用于放置默认壁纸 if (partner == null || !partner.hideDefaultWallpaper()) { // Add an entry for the default wallpaper (stored in system resources) WallpaperTileInfo defaultWallpaperInfo = (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) ? getPreKKDefaultWallpaperInfo() : getDefaultWallpaper(); if (defaultWallpaperInfo != null) { bundled.add(0, defaultWallpaperInfo); } } return bundled; }
<resources> <string-array name="wallpapers" translatable="false"> <!-- This special drawable references the platform's private default_wallpaper resource so the user can always choose it. --> <item>zzz_wallpaper</item> <item>zzz_wallpaper_small</item> <!-- If you want additional bitmap drawable resources to appear in the wallpaper picker, add them to this list. For each foo.jpg be sure to include a foo_small.jpg to be used as a thumbnail in the scrolling gallery widget. --> </string-array> </resources>加载默认壁纸,默认壁纸放在framework资源目录下
private void populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter, boolean addLongPressHandler) { for (int i = 0; i < adapter.getCount(); i++) { FrameLayout thumbnail = (FrameLayout) adapter.getView(i, null, parent); parent.addView(thumbnail, i); WallpaperTileInfo info = (WallpaperTileInfo) adapter.getItem(i); thumbnail.setTag(info); info.setView(thumbnail); if (addLongPressHandler) {// 是否添加长按事件,只对数据库中保存的壁纸处理 addLongPressHandler(thumbnail); } thumbnail.setOnClickListener(mThumbnailOnClickListener); } }这个方法比较好理解,需要注意的是第三个参数,这个布尔值用来确定该类型壁纸是否添加长按事件,这里是false,不添加;根据后面的分析来看,也只有保存在数据库中的壁纸添加该操作,这也好理解,因为其他几种类型都不是用户自己定义的,不允许删除壁纸,长按操作就是用来删除该壁纸的。
// Populate the saved wallpapers // 填充保存在数据库中的壁纸 mSavedImages = new SavedWallpaperImages(this); mSavedImages.loadThumbnailsAndImageIdList(); populateWallpapersFromAdapter(mWallpapersView, mSavedImages, true);3)添加动态壁纸
// Populate the live wallpapers // 填充动态壁纸 final LinearLayout liveWallpapersView = (LinearLayout) findViewById(R.id.live_wallpaper_list); final LiveWallpaperListAdapter a = new LiveWallpaperListAdapter(this); a.registerDataSetObserver(new DataSetObserver() { public void onChanged() { liveWallpapersView.removeAllViews(); populateWallpapersFromAdapter(liveWallpapersView, a, false); initializeScrollForRtl(); updateTileIndices(); } });
public LiveWallpaperListAdapter(Context context) { mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mPackageManager = context.getPackageManager(); List<ResolveInfo> list = mPackageManager.queryIntentServices( new Intent(WallpaperService.SERVICE_INTERFACE), PackageManager.GET_META_DATA); mWallpapers = new ArrayList<LiveWallpaperTile>(); new LiveWallpaperEnumerator(context).execute(list); }这是构造方法,查询action为"android.service.wallpaper.WallpaperService"的service,这是动态壁纸应用中必须配置的,如果我们自己想做一个动态壁纸也是要添加这个action的。
public static class LiveWallpaperTile extends WallpaperPickerActivity.WallpaperTileInfo { private Drawable mThumbnail; private WallpaperInfo mInfo; public LiveWallpaperTile(Drawable thumbnail, WallpaperInfo info, Intent intent) { mThumbnail = thumbnail; mInfo = info; } @Override public void onClick(WallpaperPickerActivity a) { Intent preview = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER); preview.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, mInfo.getComponent()); a.onLiveWallpaperPickerLaunch(mInfo); a.startActivityForResultSafely(preview, WallpaperPickerActivity.PICK_LIVE_WALLPAPER); } }WallpaperTileInfo的子类,然后异步加载信息。
for (ResolveInfo resolveInfo : list) { WallpaperInfo info = null; try { info = new WallpaperInfo(mContext, resolveInfo); } catch (XmlPullParserException e) { Log.w(LOG_TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e); continue; } catch (IOException e) { Log.w(LOG_TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e); continue; } // 获取动态壁纸信息 Drawable thumb = info.loadThumbnail(packageManager); Intent launchIntent = new Intent(WallpaperService.SERVICE_INTERFACE); launchIntent.setClassName(info.getPackageName(), info.getServiceName()); LiveWallpaperTile wallpaper = new LiveWallpaperTile(thumb, info, launchIntent); publishProgress(wallpaper); }4)第三方壁纸
// Populate the third-party wallpaper pickers // 填充第三方壁纸选择器 final LinearLayout thirdPartyWallpapersView = (LinearLayout) findViewById(R.id.third_party_wallpaper_list); final ThirdPartyWallpaperPickerListAdapter ta = new ThirdPartyWallpaperPickerListAdapter(this); populateWallpapersFromAdapter(thirdPartyWallpapersView, ta, false);
public static class ThirdPartyWallpaperTile extends WallpaperPickerActivity.WallpaperTileInfo { private ResolveInfo mResolveInfo; public ThirdPartyWallpaperTile(ResolveInfo resolveInfo) { mResolveInfo = resolveInfo; } @Override public void onClick(WallpaperPickerActivity a) { final ComponentName itemComponentName = new ComponentName( mResolveInfo.activityInfo.packageName, mResolveInfo.activityInfo.name); Intent launchIntent = new Intent(Intent.ACTION_SET_WALLPAPER); launchIntent.setComponent(itemComponentName); a.startActivityForResultSafely(launchIntent, WallpaperPickerActivity.PICK_WALLPAPER_THIRD_PARTY_ACTIVITY);// 启动第三方壁纸选择器 } }在构造方法中查询第三方壁纸应用,
public ThirdPartyWallpaperPickerListAdapter(Context context) { mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mPackageManager = context.getPackageManager(); mIconSize = context.getResources().getDimensionPixelSize(R.dimen.wallpaperItemIconSize); final PackageManager pm = mPackageManager; final Intent pickWallpaperIntent = new Intent(Intent.ACTION_SET_WALLPAPER); final List<ResolveInfo> apps = pm.queryIntentActivities(pickWallpaperIntent, 0); // Get list of image picker intents Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT); pickImageIntent.setType("image/*"); final List<ResolveInfo> imagePickerActivities = pm.queryIntentActivities(pickImageIntent, 0); final ComponentName[] imageActivities = new ComponentName[imagePickerActivities.size()]; for (int i = 0; i < imagePickerActivities.size(); i++) { ActivityInfo activityInfo = imagePickerActivities.get(i).activityInfo; imageActivities[i] = new ComponentName(activityInfo.packageName, activityInfo.name); } outerLoop: for (ResolveInfo info : apps) { final ComponentName itemComponentName = new ComponentName(info.activityInfo.packageName, info.activityInfo.name); final String itemPackageName = itemComponentName.getPackageName(); // Exclude anything from our own package, and the old Launcher, // and live wallpaper picker if (itemPackageName.equals(context.getPackageName()) || itemPackageName.equals("com.android.launcher") || itemPackageName.equals("com.android.wallpaper.livepicker")) { continue; } // Exclude any package that already responds to the image picker intent for (ResolveInfo imagePickerActivityInfo : imagePickerActivities) { if (itemPackageName.equals( imagePickerActivityInfo.activityInfo.packageName)) { continue outerLoop; } } mThirdPartyWallpaperPickers.add(new ThirdPartyWallpaperTile(info)); } }根据"android.intent.action.SET_WALLPAPER"来查找的,然后做一些过滤,添加到列表中。
// Add a tile for the Gallery // 列表开头放置图库选择器 LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list); FrameLayout pickImageTile = (FrameLayout) getLayoutInflater(). inflate(R.layout.wallpaper_picker_image_picker_item, masterWallpaperList, false); setWallpaperItemPaddingToZero(pickImageTile); masterWallpaperList.addView(pickImageTile, 0); // Make its background the last photo taken on external storage Bitmap lastPhoto = getThumbnailOfLastPhoto(); if (lastPhoto != null) { ImageView galleryThumbnailBg = (ImageView) pickImageTile.findViewById(R.id.wallpaper_image); galleryThumbnailBg.setImageBitmap(getThumbnailOfLastPhoto()); int colorOverlay = getResources().getColor(R.color.wallpaper_picker_translucent_gray); galleryThumbnailBg.setColorFilter(colorOverlay, PorterDuff.Mode.SRC_ATOP); } PickImageInfo pickImageInfo = new PickImageInfo(); pickImageTile.setTag(pickImageInfo); pickImageInfo.setView(pickImageTile); pickImageTile.setOnClickListener(mThumbnailOnClickListener);在列表开头添加图库入口,这样用户就可以选择任一图片了。
@Override public void onClick(WallpaperPickerActivity a) { Log.d("dingfeng","ResourceWallpaperInfo onClick..."); BitmapRegionTileSource.ResourceBitmapSource bitmapSource = new BitmapRegionTileSource.ResourceBitmapSource( mResources, mResId, BitmapRegionTileSource.MAX_PREVIEW_SIZE); bitmapSource.loadInBackground(); BitmapRegionTileSource source = new BitmapRegionTileSource(a, bitmapSource); CropView v = a.getCropView(); v.setTileSource(source, null); Point wallpaperSize = WallpaperCropActivity.getDefaultWallpaperSize( a.getResources(), a.getWindowManager()); RectF crop = WallpaperCropActivity.getMaxCropRect( source.getImageWidth(), source.getImageHeight(), wallpaperSize.x, wallpaperSize.y, false); v.setScale(wallpaperSize.x / crop.width()); v.setTouchEnabled(false); a.setSystemWallpaperVisiblity(false); } @Override public void onSave(WallpaperPickerActivity a) { Log.d("dingfeng","ResourceWallpaperInfo onSave..."); boolean finishActivityWhenDone = true; a.cropImageAndSetWallpaper(mResources, mResId, finishActivityWhenDone); } @Override public boolean isSelectable() { return true; } @Override public boolean isNamelessWallpaper() { return true; }
protected void cropImageAndSetWallpaper( Resources res, int resId, final boolean finishActivityWhenDone) { // crop this image and scale it down to the default wallpaper size for // this device int rotation = getRotationFromExif(res, resId); Point inSize = mCropView.getSourceDimensions(); Point outSize = getDefaultWallpaperSize(getResources(), getWindowManager()); RectF crop = getMaxCropRect(inSize.x, inSize.y, outSize.x, outSize.y, false); Runnable onEndCrop = new Runnable() { public void run() { // Passing 0, 0 will cause launcher to revert to using the // default wallpaper size updateWallpaperDimensions(0, 0); if (finishActivityWhenDone) { setResult(Activity.RESULT_OK); finish(); } } }; BitmapCropTask cropTask = new BitmapCropTask(this, res, resId, crop, rotation, outSize.x, outSize.y, true, false, onEndCrop); cropTask.execute(); }设置裁剪大小,将其作为参数传递给异步任务执行,
@Override protected Boolean doInBackground(Void... params) { return cropBitmap(); }最终就是cropBitmap方法来做最后的裁剪和壁纸设置操作。
标签:
原文地址:http://blog.csdn.net/dingfengnupt88/article/details/51833046