編輯:關於Android編程
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_WALLPAPER_SETTINGS : startWallpaper(); return true ; } return super .onOptionsItemSelected(item); }進入startWallpaper()方法,
private void startWallpaper () { showWorkspace( true); final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER ); Intent chooser = Intent.createChooser(pickWallpaper, getText(R.string. chooser_wallpaper)); // NOTE: Adds a configure option to the chooser if the wallpaper supports it // Removed in Eclair MR1 // WallpaperManager wm = (WallpaperManager) // getSystemService(Context.WALLPAPER_SERVICE); // WallpaperInfo wi = wm.getWallpaperInfo(); // if ( wi != null && wi.getSettingsActivity() != null) { // LabeledIntent li = new LabeledIntent(getPackageName(), // R.string.configure_wallpaper, 0); // li.setClassName(wi.getPackageName(), wi.getSettingsActivity()); // chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { li }); // } startActivityForResult(chooser, REQUEST_PICK_WALLPAPER ); }上面這個方法會傳遞一個選擇壁紙的Intent,然後使用Intent.createChooser打開一個界面選擇具備設置壁紙的應用。
來到Wallpaper類下,代碼如下:
/* * Copyright (C) 2007 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.gallery3d.app; import android.annotation.TargetApi; import android.app.Activity; import android.app.WallpaperManager; import android.content.ActivityNotFoundException; import android.content.Intent; import android.graphics.Point; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.view.Display; import com.android.gallery3d.common.ApiHelper; import com.android.gallery3d.filtershow.crop.CropActivity; import com.android.gallery3d.filtershow.crop.CropExtras; import java.lang.IllegalArgumentException; /** * Wallpaper picker for the gallery application. This just redirects to the * standard pick action. */ public class Wallpaper extends Activity { @SuppressWarnings("unused") private static final String TAG = "Wallpaper"; private static final String IMAGE_TYPE = "image/*"; private static final String KEY_STATE = "activity-state"; private static final String KEY_PICKED_ITEM = "picked-item"; private static final int STATE_INIT = 0; private static final int STATE_PHOTO_PICKED = 1; private int mState = STATE_INIT; private Uri mPickedItem; @Override protected void onCreate(Bundle bundle) { super.onCreate(bundle); if (bundle != null) { mState = bundle.getInt(KEY_STATE); mPickedItem = (Uri) bundle.getParcelable(KEY_PICKED_ITEM); } } @Override protected void onSaveInstanceState(Bundle saveState) { saveState.putInt(KEY_STATE, mState); if (mPickedItem != null) { saveState.putParcelable(KEY_PICKED_ITEM, mPickedItem); } } @SuppressWarnings("deprecation") @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) private Point getDefaultDisplaySize(Point size) { Display d = getWindowManager().getDefaultDisplay(); if (Build.VERSION.SDK_INT >= ApiHelper.VERSION_CODES.HONEYCOMB_MR2) { d.getSize(size); } else { size.set(d.getWidth(), d.getHeight()); } return size; } @SuppressWarnings("fallthrough") @Override protected void onResume() { super.onResume(); Intent intent = getIntent(); switch (mState) { case STATE_INIT: { mPickedItem = intent.getData(); if (mPickedItem == null) { Intent request = new Intent(Intent.ACTION_GET_CONTENT) .setClass(this, DialogPicker.class) .setType(IMAGE_TYPE); startActivityForResult(request, STATE_PHOTO_PICKED); return; } mState = STATE_PHOTO_PICKED; // fall-through } case STATE_PHOTO_PICKED: { Intent cropAndSetWallpaperIntent; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { WallpaperManager wpm = WallpaperManager.getInstance(getApplicationContext()); try { cropAndSetWallpaperIntent = wpm.getCropAndSetWallpaperIntent(mPickedItem); startActivity(cropAndSetWallpaperIntent); finish(); return; } catch (ActivityNotFoundException anfe) { // ignored; fallthru to existing crop activity } catch (IllegalArgumentException iae) { // ignored; fallthru to existing crop activity } } int width = getWallpaperDesiredMinimumWidth(); int height = getWallpaperDesiredMinimumHeight(); Point size = getDefaultDisplaySize(new Point()); float spotlightX = (float) size.x / width; float spotlightY = (float) size.y / height; cropAndSetWallpaperIntent = new Intent(CropActivity.CROP_ACTION) .setClass(this, CropActivity.class) .setDataAndType(mPickedItem, IMAGE_TYPE) .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT) .putExtra(CropExtras.KEY_OUTPUT_X, width) .putExtra(CropExtras.KEY_OUTPUT_Y, height) .putExtra(CropExtras.KEY_ASPECT_X, width) .putExtra(CropExtras.KEY_ASPECT_Y, height) .putExtra(CropExtras.KEY_SPOTLIGHT_X, spotlightX) .putExtra(CropExtras.KEY_SPOTLIGHT_Y, spotlightY) .putExtra(CropExtras.KEY_SCALE, true) .putExtra(CropExtras.KEY_SCALE_UP_IF_NEEDED, true) .putExtra(CropExtras.KEY_SET_AS_WALLPAPER, true); startActivity(cropAndSetWallpaperIntent); finish(); } } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode != RESULT_OK) { setResult(resultCode); finish(); return; } mState = requestCode; if (mState == STATE_PHOTO_PICKED) { mPickedItem = data.getData(); } // onResume() would be called next } }這個類的邏輯比較簡單,先是第一次進入Activity然後進入onResume()方法,然後進入 STATE_INIT。在這裡選擇一張照片,然後返回。接著調用onActivityResult方法,用於獲取mPickedItem值,之後再次調用onResume()方法,然後進入STATE_PHOTO_PICKED。先是一個判斷,如果是KitKat以上版本,那麼會調用新的方法getCropAndSetWallpaperIntent去完成壁紙的設置,界面如下:圖-5 如果是KitKat以下版本,那麼會調用以下方法:
int width = getWallpaperDesiredMinimumWidth(); int height = getWallpaperDesiredMinimumHeight(); Point size = getDefaultDisplaySize( new Point()); float spotlightX = (float) size.x / width; float spotlightY = (float) size.y / height; cropAndSetWallpaperIntent = new Intent(CropActivity.CROP_ACTION ) .setClass( this, CropActivity.class) .setDataAndType( mPickedItem, IMAGE_TYPE) .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT ) .putExtra(CropExtras. KEY_OUTPUT_X, width) .putExtra(CropExtras. KEY_OUTPUT_Y, height) .putExtra(CropExtras. KEY_ASPECT_X, width) .putExtra(CropExtras. KEY_ASPECT_Y, height) .putExtra(CropExtras. KEY_SPOTLIGHT_X, spotlightX) .putExtra(CropExtras. KEY_SPOTLIGHT_Y, spotlightY) .putExtra(CropExtras. KEY_SCALE, true ) .putExtra(CropExtras.KEY_SCALE_UP_IF_NEEDED , true) .putExtra(CropExtras.KEY_SET_AS_WALLPAPER , true); startActivity(cropAndSetWallpaperIntent); finish();先是調用系統方法獲取期望的壁紙的最小寬度和最小高度。然後是CropExtras這個類,這個實體類描述了剪裁壁紙的信息,包括長寬、縮放、格式等等。代碼如下:
/* * Copyright (C) 2012 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.gallery3d.filtershow.crop; import android.net.Uri; public class CropExtras { public static final String KEY_CROPPED_RECT = "cropped-rect"; public static final String KEY_OUTPUT_X = "outputX"; public static final String KEY_OUTPUT_Y = "outputY"; public static final String KEY_SCALE = "scale"; public static final String KEY_SCALE_UP_IF_NEEDED = "scaleUpIfNeeded"; public static final String KEY_ASPECT_X = "aspectX"; public static final String KEY_ASPECT_Y = "aspectY"; public static final String KEY_SET_AS_WALLPAPER = "set-as-wallpaper"; public static final String KEY_RETURN_DATA = "return-data"; public static final String KEY_DATA = "data"; public static final String KEY_SPOTLIGHT_X = "spotlightX"; public static final String KEY_SPOTLIGHT_Y = "spotlightY"; public static final String KEY_SHOW_WHEN_LOCKED = "showWhenLocked"; public static final String KEY_OUTPUT_FORMAT = "outputFormat"; private int mOutputX = 0; private int mOutputY = 0; private boolean mScaleUp = true; private int mAspectX = 0; private int mAspectY = 0; private boolean mSetAsWallpaper = false; private boolean mReturnData = false; private Uri mExtraOutput = null; private String mOutputFormat = null; private boolean mShowWhenLocked = false; private float mSpotlightX = 0; private float mSpotlightY = 0; public CropExtras(int outputX, int outputY, boolean scaleUp, int aspectX, int aspectY, boolean setAsWallpaper, boolean returnData, Uri extraOutput, String outputFormat, boolean showWhenLocked, float spotlightX, float spotlightY) { mOutputX = outputX; mOutputY = outputY; mScaleUp = scaleUp; mAspectX = aspectX; mAspectY = aspectY; mSetAsWallpaper = setAsWallpaper; mReturnData = returnData; mExtraOutput = extraOutput; mOutputFormat = outputFormat; mShowWhenLocked = showWhenLocked; mSpotlightX = spotlightX; mSpotlightY = spotlightY; } public CropExtras(CropExtras c) { this(c.mOutputX, c.mOutputY, c.mScaleUp, c.mAspectX, c.mAspectY, c.mSetAsWallpaper, c.mReturnData, c.mExtraOutput, c.mOutputFormat, c.mShowWhenLocked, c.mSpotlightX, c.mSpotlightY); } public int getOutputX() { return mOutputX; } public int getOutputY() { return mOutputY; } public boolean getScaleUp() { return mScaleUp; } public int getAspectX() { return mAspectX; } public int getAspectY() { return mAspectY; } public boolean getSetAsWallpaper() { return mSetAsWallpaper; } public boolean getReturnData() { return mReturnData; } public Uri getExtraOutput() { return mExtraOutput; } public String getOutputFormat() { return mOutputFormat; } public boolean getShowWhenLocked() { return mShowWhenLocked; } public float getSpotlightX() { return mSpotlightX; } public float getSpotlightY() { return mSpotlightY; } }不過對於類成員屬性我們並不清楚各自代表什麼意思,不過從傳遞進去的參數來看,是可以發現一些端倪的。比如KEY_OUTPUT_X現在可以簡單的理解為width,同理高度也一樣。 進入CropActivity的時候會把這些信息傳遞過去。CropActivity就是上面那個可以縮放設置壁紙的界面,完成這個功能的那個其實是一個自定義View:CropView。接著來到CropActivity,這就是圖-3所顯示的Activity。這個類所在包以及其他類如下:
/* * Copyright (C) 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. */ package com.android.gallery3d.filtershow.crop; import android.app.ActionBar; import android.app.Activity; import android.app.WallpaperManager; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.provider.MediaStore; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.view.WindowManager; import android.widget.Toast; import com.android.gallery3d.R; import com.android.gallery3d.common.Utils; import com.android.gallery3d.filtershow.cache.ImageLoader; import com.android.gallery3d.filtershow.tools.SaveImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Activity for cropping an image. */ public class CropActivity extends Activity { private static final String LOGTAG = "CropActivity"; public static final String CROP_ACTION = "com.android.camera.action.CROP"; // 剪裁信息 private CropExtras mCropExtras = null; // 加載Bitmap private LoadBitmapTask mLoadBitmapTask = null; private int mOutputX = 0; private int mOutputY = 0; private Bitmap mOriginalBitmap = null; private RectF mOriginalBounds = null; private int mOriginalRotation = 0; private Uri mSourceUri = null; // 自定義的View 剪裁壁紙 private CropView mCropView = null; // 保存的button private View mSaveButton = null; // 保護IO操作 private boolean finalIOGuard = false; private static final int SELECT_PICTURE = 1; // request code for picker private static final int DEFAULT_COMPRESS_QUALITY = 90; /** * The maximum bitmap size we allow to be returned through the intent. * Intents have a maximum of 1MB in total size. However, the Bitmap seems to * have some overhead to hit so that we go way below the limit here to make * sure the intent stays below 1MB.We should consider just returning a byte * array instead of a Bitmap instance to avoid overhead. */ public static final int MAX_BMAP_IN_INTENT = 750000; // Flags private static final int DO_SET_WALLPAPER = 1; private static final int DO_RETURN_DATA = 1 << 1; private static final int DO_EXTRA_OUTPUT = 1 << 2; private static final int FLAG_CHECK = DO_SET_WALLPAPER | DO_RETURN_DATA | DO_EXTRA_OUTPUT; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Intent intent = getIntent(); setResult(RESULT_CANCELED, new Intent()); mCropExtras = getExtrasFromIntent(intent); // if (mCropExtras != null && mCropExtras.getShowWhenLocked()) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); } setContentView(R.layout.crop_activity); mCropView = (CropView) findViewById(R.id.cropView); // 自定義ActionBar布局 添加的Button用於保存設置壁紙 ActionBar actionBar = getActionBar(); if (actionBar != null) { actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); actionBar.setCustomView(R.layout.filtershow_actionbar); View mSaveButton = actionBar.getCustomView(); mSaveButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { // 保存設置剪裁的壁紙 startFinishOutput(); } }); } if (intent.getData() != null) { mSourceUri = intent.getData(); // 開始加載之前選擇的圖片 startLoadBitmap(mSourceUri); } else { // 否則回去選圖片 pickImage(); } } private void enableSave(boolean enable) { if (mSaveButton != null) { mSaveButton.setEnabled(enable); } } @Override protected void onDestroy() { // 取消加載Bitmap的任務 if (mLoadBitmapTask != null) { mLoadBitmapTask.cancel(false); } super.onDestroy(); } @Override public void onConfigurationChanged (Configuration newConfig) { super.onConfigurationChanged(newConfig); mCropView.configChanged(); } /** * Opens a selector in Gallery to chose an image for use when none was given * in the CROP intent. */ private void pickImage() { Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)), SELECT_PICTURE); } /** * Callback for pickImage(). */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK && requestCode == SELECT_PICTURE) { mSourceUri = data.getData(); startLoadBitmap(mSourceUri); } } /** * Gets screen size metric. * 通過獲取屏幕的寬和高 返回二者中的最大值 一般的豎屏手機肯定是返回高度了 */ private int getScreenImageSize() { DisplayMetrics outMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(outMetrics); return (int) Math.max(outMetrics.heightPixels, outMetrics.widthPixels); } /** * Method that loads a bitmap in an async task. * 啟動一個一步任務去加載Bitmap 這個Bitmap就是之前我們選擇要設置為壁紙的圖片 */ private void startLoadBitmap(Uri uri) { if (uri != null) { enableSave(false); final View loading = findViewById(R.id.loading); loading.setVisibility(View.VISIBLE); mLoadBitmapTask = new LoadBitmapTask(); mLoadBitmapTask.execute(uri); } else { cannotLoadImage(); done(); } } /** * Method called on UI thread with loaded bitmap. * 當加載完Bitmap之後將Bitmap賦值給mCropView 接著它負責圖像的剪裁工作 */ private void doneLoadBitmap(Bitmap bitmap, RectF bounds, int orientation) { final View loading = findViewById(R.id.loading); loading.setVisibility(View.GONE); mOriginalBitmap = bitmap; mOriginalBounds = bounds; mOriginalRotation = orientation; if (bitmap != null && bitmap.getWidth() != 0 && bitmap.getHeight() != 0) { RectF imgBounds = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()); mCropView.initialize(bitmap, imgBounds, imgBounds, orientation); if (mCropExtras != null) { int aspectX = mCropExtras.getAspectX(); int aspectY = mCropExtras.getAspectY(); mOutputX = mCropExtras.getOutputX(); mOutputY = mCropExtras.getOutputY(); if (mOutputX > 0 && mOutputY > 0) { mCropView.applyAspect(mOutputX, mOutputY); } float spotX = mCropExtras.getSpotlightX(); float spotY = mCropExtras.getSpotlightY(); if (spotX > 0 && spotY > 0) { mCropView.setWallpaperSpotlight(spotX, spotY); } if (aspectX > 0 && aspectY > 0) { mCropView.applyAspect(aspectX, aspectY); } } enableSave(true); } else { Log.w(LOGTAG, "could not load image for cropping"); cannotLoadImage(); setResult(RESULT_CANCELED, new Intent()); done(); } } /** * Display toast for image loading failure. * 無法加載圖片 */ private void cannotLoadImage() { CharSequence text = getString(R.string.cannot_load_image); Toast toast = Toast.makeText(this, text, Toast.LENGTH_SHORT); toast.show(); } /** * AsyncTask for loading a bitmap into memory. * 異步任務加載bitmap到內存 * @see #startLoadBitmap(Uri) * @see #doneLoadBitmap(Bitmap) */ private class LoadBitmapTask extends AsyncTaskCropActivity的處理邏輯是這樣,首先開啟一個異步任務根據傳遞過來的信息去加載壁紙Bitmap。加載完畢再開啟一個異步任務去處理這個Bitmap,處理完最後設置為壁紙。代碼已經添加了注釋。 設置完畢返回到桌面,會有1到2秒的設置時間,之後壁紙就設置好了。設置好的壁紙是可以隨著桌面滑動的,這麼說不太准確,應該說是Launcher讓壁紙隨著桌面滑動而滑動(parallax effects)。處理邏輯是在Workspace類裡,由於Workspace類代碼較長就不貼代碼了,找相關代碼分析。變量:{ int mBitmapSize; Context mContext; Rect mOriginalBounds; int mOrientation; public LoadBitmapTask() { mBitmapSize = getScreenImageSize(); mContext = getApplicationContext(); mOriginalBounds = new Rect(); mOrientation = 0; } @Override protected Bitmap doInBackground(Uri... params) { Uri uri = params[0]; Bitmap bmap = ImageLoader.loadConstrainedBitmap(uri, mContext, mBitmapSize, mOriginalBounds, false); mOrientation = ImageLoader.getMetadataRotation(mContext, uri); return bmap; } @Override protected void onPostExecute(Bitmap result) { doneLoadBitmap(result, new RectF(mOriginalBounds), mOrientation); } } /** * */ protected void startFinishOutput() { if (finalIOGuard) { return; } else { finalIOGuard = true; } enableSave(false); Uri destinationUri = null; int flags = 0; if (mOriginalBitmap != null && mCropExtras != null) { if (mCropExtras.getExtraOutput() != null) { destinationUri = mCropExtras.getExtraOutput(); if (destinationUri != null) { flags |= DO_EXTRA_OUTPUT; } } if (mCropExtras.getSetAsWallpaper()) { flags |= DO_SET_WALLPAPER; } if (mCropExtras.getReturnData()) { flags |= DO_RETURN_DATA; } } if (flags == 0) { destinationUri = SaveImage.makeAndInsertUri(this, mSourceUri); if (destinationUri != null) { flags |= DO_EXTRA_OUTPUT; } } if ((flags & FLAG_CHECK) != 0 && mOriginalBitmap != null) { RectF photo = new RectF(0, 0, mOriginalBitmap.getWidth(), mOriginalBitmap.getHeight()); RectF crop = getBitmapCrop(photo); startBitmapIO(flags, mOriginalBitmap, mSourceUri, destinationUri, crop, photo, mOriginalBounds, (mCropExtras == null) ? null : mCropExtras.getOutputFormat(), mOriginalRotation); return; } setResult(RESULT_CANCELED, new Intent()); done(); return; } /** * @category 開始BitmapIOTask任務 * @param flags * @param currentBitmap * @param sourceUri * @param destUri * @param cropBounds * @param photoBounds * @param currentBitmapBounds * @param format * @param rotation */ private void startBitmapIO(int flags, Bitmap currentBitmap, Uri sourceUri, Uri destUri, RectF cropBounds, RectF photoBounds, RectF currentBitmapBounds, String format, int rotation) { if (cropBounds == null || photoBounds == null || currentBitmap == null || currentBitmap.getWidth() == 0 || currentBitmap.getHeight() == 0 || cropBounds.width() == 0 || cropBounds.height() == 0 || photoBounds.width() == 0 || photoBounds.height() == 0) { return; // fail fast } if ((flags & FLAG_CHECK) == 0) { return; // no output options } if ((flags & DO_SET_WALLPAPER) != 0) { Toast.makeText(this, R.string.setting_wallpaper, Toast.LENGTH_LONG).show(); } final View loading = findViewById(R.id.loading); loading.setVisibility(View.VISIBLE); BitmapIOTask ioTask = new BitmapIOTask(sourceUri, destUri, format, flags, cropBounds, photoBounds, currentBitmapBounds, rotation, mOutputX, mOutputY); ioTask.execute(currentBitmap); } /** * 完成BitmapIO任務 */ private void doneBitmapIO(boolean success, Intent intent) { final View loading = findViewById(R.id.loading); loading.setVisibility(View.GONE); if (success) { setResult(RESULT_OK, intent); } else { setResult(RESULT_CANCELED, intent); } done(); } /** * 對傳遞進來的Bimap進行處理,然後設置成壁紙 */ private class BitmapIOTask extends AsyncTask { private final WallpaperManager mWPManager; InputStream mInStream = null; OutputStream mOutStream = null; String mOutputFormat = null; Uri mOutUri = null; Uri mInUri = null; int mFlags = 0; RectF mCrop = null; RectF mPhoto = null; RectF mOrig = null; Intent mResultIntent = null; int mRotation = 0; // Helper to setup input stream private void regenerateInputStream() { if (mInUri == null) { Log.w(LOGTAG, "cannot read original file, no input URI given"); } else { Utils.closeSilently(mInStream); try { mInStream = getContentResolver().openInputStream(mInUri); } catch (FileNotFoundException e) { Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e); } } } public BitmapIOTask(Uri sourceUri, Uri destUri, String outputFormat, int flags, RectF cropBounds, RectF photoBounds, RectF originalBitmapBounds, int rotation, int outputX, int outputY) { mOutputFormat = outputFormat; mOutStream = null; mOutUri = destUri; mInUri = sourceUri; mFlags = flags; mCrop = cropBounds; mPhoto = photoBounds; mOrig = originalBitmapBounds; mWPManager = WallpaperManager.getInstance(getApplicationContext()); mResultIntent = new Intent(); mRotation = (rotation < 0) ? -rotation : rotation; mRotation %= 360; mRotation = 90 * (int) (mRotation / 90); // now mRotation is a multiple of 90 mOutputX = outputX; mOutputY = outputY; if ((flags & DO_EXTRA_OUTPUT) != 0) { if (mOutUri == null) { Log.w(LOGTAG, "cannot write file, no output URI given"); } else { try { mOutStream = getContentResolver().openOutputStream(mOutUri); } catch (FileNotFoundException e) { Log.w(LOGTAG, "cannot write file: " + mOutUri.toString(), e); } } } if ((flags & (DO_EXTRA_OUTPUT | DO_SET_WALLPAPER)) != 0) { regenerateInputStream(); } } @Override protected Boolean doInBackground(Bitmap... params) { boolean failure = false; Bitmap img = params[0]; // Set extra for crop bounds if (mCrop != null && mPhoto != null && mOrig != null) { RectF trueCrop = CropMath.getScaledCropBounds(mCrop, mPhoto, mOrig); Matrix m = new Matrix(); m.setRotate(mRotation); m.mapRect(trueCrop); if (trueCrop != null) { Rect rounded = new Rect(); trueCrop.roundOut(rounded); mResultIntent.putExtra(CropExtras.KEY_CROPPED_RECT, rounded); } } // Find the small cropped bitmap that is returned in the intent if ((mFlags & DO_RETURN_DATA) != 0) { assert (img != null); Bitmap ret = getCroppedImage(img, mCrop, mPhoto); if (ret != null) { ret = getDownsampledBitmap(ret, MAX_BMAP_IN_INTENT); } if (ret == null) { Log.w(LOGTAG, "could not downsample bitmap to return in data"); failure = true; } else { if (mRotation > 0) { Matrix m = new Matrix(); m.setRotate(mRotation); Bitmap tmp = Bitmap.createBitmap(ret, 0, 0, ret.getWidth(), ret.getHeight(), m, true); if (tmp != null) { ret = tmp; } } mResultIntent.putExtra(CropExtras.KEY_DATA, ret); } } // Do the large cropped bitmap and/or set the wallpaper if ((mFlags & (DO_EXTRA_OUTPUT | DO_SET_WALLPAPER)) != 0 && mInStream != null) { // Find crop bounds (scaled to original image size) RectF trueCrop = CropMath.getScaledCropBounds(mCrop, mPhoto, mOrig); if (trueCrop == null) { Log.w(LOGTAG, "cannot find crop for full size image"); failure = true; return false; } Rect roundedTrueCrop = new Rect(); trueCrop.roundOut(roundedTrueCrop); if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { Log.w(LOGTAG, "crop has bad values for full size image"); failure = true; return false; } // Attempt to open a region decoder BitmapRegionDecoder decoder = null; try { decoder = BitmapRegionDecoder.newInstance(mInStream, true); } catch (IOException e) { Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e); } Bitmap crop = null; if (decoder != null) { // Do region decoding to get crop bitmap BitmapFactory.Options options = new BitmapFactory.Options(); options.inMutable = true; crop = decoder.decodeRegion(roundedTrueCrop, options); decoder.recycle(); } if (crop == null) { // BitmapRegionDecoder has failed, try to crop in-memory regenerateInputStream(); Bitmap fullSize = null; if (mInStream != null) { fullSize = BitmapFactory.decodeStream(mInStream); } if (fullSize != null) { crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, roundedTrueCrop.top, roundedTrueCrop.width(), roundedTrueCrop.height()); } } if (crop == null) { Log.w(LOGTAG, "cannot decode file: " + mInUri.toString()); failure = true; return false; } if (mOutputX > 0 && mOutputY > 0) { Matrix m = new Matrix(); RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight()); if (mRotation > 0) { m.setRotate(mRotation); m.mapRect(cropRect); } RectF returnRect = new RectF(0, 0, mOutputX, mOutputY); m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); m.preRotate(mRotation); Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), (int) returnRect.height(), Bitmap.Config.ARGB_8888); if (tmp != null) { Canvas c = new Canvas(tmp); c.drawBitmap(crop, m, new Paint()); crop = tmp; } } else if (mRotation > 0) { Matrix m = new Matrix(); m.setRotate(mRotation); Bitmap tmp = Bitmap.createBitmap(crop, 0, 0, crop.getWidth(), crop.getHeight(), m, true); if (tmp != null) { crop = tmp; } } // Get output compression format CompressFormat cf = convertExtensionToCompressFormat(getFileExtension(mOutputFormat)); // If we only need to output to a URI, compress straight to file if (mFlags == DO_EXTRA_OUTPUT) { if (mOutStream == null || !crop.compress(cf, DEFAULT_COMPRESS_QUALITY, mOutStream)) { Log.w(LOGTAG, "failed to compress bitmap to file: " + mOutUri.toString()); failure = true; } else { mResultIntent.setData(mOutUri); } } else { // Compress to byte array ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048); if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) { // If we need to output to a Uri, write compressed // bitmap out if ((mFlags & DO_EXTRA_OUTPUT) != 0) { if (mOutStream == null) { Log.w(LOGTAG, "failed to compress bitmap to file: " + mOutUri.toString()); failure = true; } else { try { mOutStream.write(tmpOut.toByteArray()); mResultIntent.setData(mOutUri); } catch (IOException e) { Log.w(LOGTAG, "failed to compress bitmap to file: " + mOutUri.toString(), e); failure = true; } } } // If we need to set to the wallpaper, set it if ((mFlags & DO_SET_WALLPAPER) != 0 && mWPManager != null) { if (mWPManager == null) { Log.w(LOGTAG, "no wallpaper manager"); failure = true; } else { try { mWPManager.setStream(new ByteArrayInputStream(tmpOut .toByteArray())); } catch (IOException e) { Log.w(LOGTAG, "cannot write stream to wallpaper", e); failure = true; } } } } else { Log.w(LOGTAG, "cannot compress bitmap"); failure = true; } } } return !failure; // True if any of the operations failed } @Override protected void onPostExecute(Boolean result) { Utils.closeSilently(mOutStream); Utils.closeSilently(mInStream); doneBitmapIO(result.booleanValue(), mResultIntent); } } private void done() { finish(); } /** * @category 返回CroppedImage * @param image * @param cropBounds * @param photoBounds * @return */ protected static Bitmap getCroppedImage(Bitmap image, RectF cropBounds, RectF photoBounds) { RectF imageBounds = new RectF(0, 0, image.getWidth(), image.getHeight()); RectF crop = CropMath.getScaledCropBounds(cropBounds, photoBounds, imageBounds); if (crop == null) { return null; } Rect intCrop = new Rect(); crop.roundOut(intCrop); return Bitmap.createBitmap(image, intCrop.left, intCrop.top, intCrop.width(), intCrop.height()); } protected static Bitmap getDownsampledBitmap(Bitmap image, int max_size) { if (image == null || image.getWidth() == 0 || image.getHeight() == 0 || max_size < 16) { throw new IllegalArgumentException("Bad argument to getDownsampledBitmap()"); } int shifts = 0; int size = CropMath.getBitmapSize(image); while (size > max_size) { shifts++; size /= 4; } Bitmap ret = Bitmap.createScaledBitmap(image, image.getWidth() >> shifts, image.getHeight() >> shifts, true); if (ret == null) { return null; } // Handle edge case for rounding. if (CropMath.getBitmapSize(ret) > max_size) { return Bitmap.createScaledBitmap(ret, ret.getWidth() >> 1, ret.getHeight() >> 1, true); } return ret; } /** * Gets the crop extras from the intent, or null if none exist. */ protected static CropExtras getExtrasFromIntent(Intent intent) { Bundle extras = intent.getExtras(); if (extras != null) { return new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0), extras.getInt(CropExtras.KEY_OUTPUT_Y, 0), extras.getBoolean(CropExtras.KEY_SCALE, true) && extras.getBoolean(CropExtras.KEY_SCALE_UP_IF_NEEDED, false), extras.getInt(CropExtras.KEY_ASPECT_X, 0), extras.getInt(CropExtras.KEY_ASPECT_Y, 0), extras.getBoolean(CropExtras.KEY_SET_AS_WALLPAPER, false), extras.getBoolean(CropExtras.KEY_RETURN_DATA, false), (Uri) extras.getParcelable(MediaStore.EXTRA_OUTPUT), extras.getString(CropExtras.KEY_OUTPUT_FORMAT), extras.getBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, false), extras.getFloat(CropExtras.KEY_SPOTLIGHT_X), extras.getFloat(CropExtras.KEY_SPOTLIGHT_Y)); } return null; } protected static CompressFormat convertExtensionToCompressFormat(String extension) { return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG; } protected static String getFileExtension(String requestFormat) { String outputFormat = (requestFormat == null) ? "jpg" : requestFormat; outputFormat = outputFormat.toLowerCase(); return (outputFormat.equals("png") || outputFormat.equals("gif")) ? "png" // We don't support gif compression. : "jpg"; } private RectF getBitmapCrop(RectF imageBounds) { RectF crop = mCropView.getCrop(); RectF photo = mCropView.getPhoto(); if (crop == null || photo == null) { Log.w(LOGTAG, "could not get crop"); return null; } RectF scaledCrop = CropMath.getScaledCropBounds(crop, photo, imageBounds); return scaledCrop; } }
private float mWallpaperScrollRatio = 1.0f; private int mOriginalPageSpacing ; private final WallpaperManager mWallpaperManager; private IBinder mWindowToken; private static final float WALLPAPER_SCREENS_SPAN = 2f; enum WallpaperVerticalOffset { TOP, MIDDLE , BOTTOM }; int mWallpaperWidth; int mWallpaperHeight; WallpaperOffsetInterpolator mWallpaperOffset; boolean mUpdateWallpaperOffsetImmediately = false ; private Runnable mDelayedResizeRunnable; private Runnable mDelayedSnapToPageRunnable; private Point mDisplaySize = new Point(); private boolean mIsStaticWallpaper ; private int mWallpaperTravelWidth ; private int mSpringLoadedPageSpacing ; private int mCameraDistance ; mWallpaperManager = WallpaperManager.getInstance(context); mWallpaperOffset = new WallpaperOffsetInterpolator(); mWallpaperTravelWidth = (int) (mDisplaySize. x * wallpaperTravelToScreenWidthRatio(mDisplaySize.x , mDisplaySize .y )); mIsStaticWallpaper = mWallpaperManager.getWallpaperInfo() == null ;方法和類:
setWallpaperDimension() wallpaperTravelToScreenWidthRatio() wallpaperOffsetForCurrentScroll() syncWallpaperOffsetWithScroll() updateWallpaperOffsetImmediately() updateWallpaperOffsets() computeWallpaperScrollRatio() WallpaperOffsetInterpolator類可以說是WallpaperOffsetInterpolator負責壁紙滑動的,其他變量和方法為它服務。setWallpaperDimension()方法是設置壁紙的大小,這個方法在Launcher類初始化時候會調用。wallpaperOffsetForCurrentScroll()方法用於計算滑動壁紙需要移動的距離,它在syncWallpaperOffsetWithScroll()方法中調用,syncWallpaperOffsetWithScroll()方法很簡單,如果手機開啟硬件加速就執行WallpaperOffsetInterpolator的setFinalX方法,setFinalX方法再去調用wallpaperOffsetForCurrentScroll()方法。
private void syncWallpaperOffsetWithScroll() { final boolean enableWallpaperEffects = isHardwareAccelerated(); if (enableWallpaperEffects) { mWallpaperOffset.setFinalX(wallpaperOffsetForCurrentScroll()); } }如果我們把enableWallpaperEffects值改為false會怎麼樣呢,沒錯就是固定不動的壁紙了。updateWallpaperOffsets()方法用於在ondraw()方法中去更新視圖(滑動的時候),配合在computeScroll()方法中調用syncWallpaperOffsetWithScroll()方法。
@Override public void computeScroll() { super .computeScroll(); syncWallpaperOffsetWithScroll(); }關於Launcher設置壁紙的流程就先介紹到這裡,有疏漏的地方還請指正。有功能就會有Bug,那麼一般關於壁紙常見的Bug有哪些呢?下一篇文章會具體介紹,有這樣的經驗的朋友歡迎一切探討、交流。
我這裡只是簡單的用了兩個listview來實現的,先上效果圖。比較粗糙。預留了自定義的空間。思路:從上圖應該可以看的出來。就是上下兩個listview。點擊下面的ltem
本文實例講述了Android控件之GridView用法。分享給大家供大家參考。具體如下:GridView是一項顯示二維的viewgroup,可滾動的網格。一般用來顯示多張
上篇中介紹了MediaPlayer從prepare到playback的其他過程,但是很多的一些音視頻的基礎概念可能還不是很清楚,今天將介紹下對於多媒體開發時,常常有一些基
首先,語音合成是指將文本信息轉換成聲音。意思就是將文本轉化為聲音,讓你的應用開口說話。國內在業內比較有名的第三方語音合成平台有百度語音和科大訊飛。本文集成的是百度語音合成