For my application, I have a texture in high resolution. To reduce the size for small screens I do like that:
#Override
public void onLoadResources(){
Options options = new BitmapFactory.Options();
options.inScaled = false;
// calculation inSampleSize
int sm = 1;
if (cameraWidth+cameraHeight < 1280) sm = 2;// < 800x480
if (cameraWidth+cameraHeight < 800) sm = 4;// < 480x320
options.inSampleSize = sm;
mTexture = new BitmapTextureAtlas(2048/sm, 2048/sm, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
// Loading bitmap
sky_bm = BitmapFactory.decodeResource(getResources(), R.drawable.sky, options);
sky_src = new BitmapTextureAtlasSource(sky_bm);
skyRegion = TextureRegionFactory.createFromSource(mTexture, sky_src, 0, 0, false);
mEngine.getTextureManager().loadTexture(mTexture);
BitmapTextureAtlasSource code:
public class BitmapTextureAtlasSource extends BaseTextureAtlasSource implements IBitmapTextureAtlasSource {
private Bitmap mBitmap;
public BitmapTextureAtlasSource(Bitmap pBitmap) {
super(0,0);
//this.mBitmap = pBitmap;
this.mBitmap = pBitmap.copy(Bitmap.Config.ARGB_8888, false);
}
public int getWidth() {
return mBitmap.getWidth();
}
public int getHeight() {
return mBitmap.getHeight();
}
#Override
public BitmapTextureAtlasSource clone() {
return new BitmapTextureAtlasSource(Bitmap.createBitmap(mBitmap));
}
public Bitmap onLoadBitmap(Config pBitmapConfig) {
return mBitmap;
}
#Override
public IBitmapTextureAtlasSource deepCopy() {
return null;
}
}
But when rotating the screen, I get the error:
FATAL EXCEPTION: GLThread 4895
java.lang.IllegalArgumentException: bitmap is recycled
at android.opengl.GLUtils.texSubImage2D(GLUtils.java:220)
at org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas.writeTextureToHardware(BitmapTextureAtlas.java:162)
at org.anddev.andengine.opengl.texture.Texture.loadToHardware(Texture.java:116)
at org.anddev.andengine.opengl.texture.TextureManager.updateTextures(TextureManager.java:146)
at org.anddev.andengine.engine.Engine.onDrawFrame(Engine.java:507)
at org.anddev.andengine.opengl.view.RenderSurfaceView$Renderer.onDrawFrame(RenderSurfaceView.java:154)
at net.rbgrn.opengl.GLThread.guardedRun(GLThread.java:235)
at net.rbgrn.opengl.GLThread.run(GLThread.java:94)
Please tell me what I'm doing wrong. I would be grateful for any information
I think the problem could be on onLoadBitmap, you should return a copy. I suggest you to try this implementation extending EmptyBitmapTextureAtlasSource:
public class BitmapTextureSource extends EmptyBitmapTextureAtlasSource {
private Bitmap mBitmap;
public BitmapTextureSource(Bitmap bitmap) {
super(bitmap.getWidth(), bitmap.getHeight());
mBitmap = bitmap;
}
#Override
public Bitmap onLoadBitmap(Config pBitmapConfig) {
return mBitmap.copy(pBitmapConfig, true);
}
}
I suggest you set your resolution policy to RatioResolutionPolicy and let the engine do the scaling for you.
http://andengine.wikidot.com/detect-screen-resolution
Related
I know that there are countless examples of this on the internet, but for some reason I can't get this to work in the particular code I'm working on, despite all the resources/answers I've read through. And I don't have anyone with Java skills around me to help out. So hopefully that changes here.
I'm updating an existing Android media picker plugin (for a cordova app) to make it show video thumbnails in addition to pictures in the device gallery. I'm stuck with a "non-static variable in static context" error, but I'm having a really hard time identifying what needs to be changed. Below is the meat of the code I have. I have removed some parts to hopefully focus on the relevant bits. Essentially, the error occurs inside decodeSampledBitmapFromUri when I'm trying to get the thumbnail of a video. The method I'm using is MediaStore.Video.Thumbnails.getThumbnail and its first argument is the context, which is where the error starts. You can see that in loadThumbnail I tried getting the context using cordova.getActivity() and then passing it to decodeSampledBitmapFromUri, but even inside loadThumbnail I'm still getting the non-static error. I'm not sure how to proceed from here (I'm very new to Java). This is the code (with some other parts stripped out because I think they're not relevant):
public class MediaPicker extends CordovaPlugin {
private static final String HEIGHT = "height";
private static final String COLUMNS = "columns";
private static final String SELECTION_LIMIT = "selectionLimit";
private MediaPickerView view;
public static int getResourceId(Context context, String group, String key) {
return context.getResources().getIdentifier(key, group, context.getPackageName());
}
public static int DP2PX(Context context, float dipValue) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, metrics);
}
#Override
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
}
#Override
public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException {
if (action.equals("display")) {
JSONObject store = data.getJSONObject(0);
double height = Double.parseDouble(store.getString(HEIGHT));
int columns = Integer.parseInt(store.getString(COLUMNS));
int selectionLimit = Integer.parseInt(store.getString(SELECTION_LIMIT));
display(height, columns, selectionLimit, callbackContext);
return true;
} else {
return false;
}
}
private void display(final double height, final int columns, final int selectionLimit, final CallbackContext callbackContext) {
cordova.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
if (view == null) {
view = new MediaPickerView(cordova.getActivity());
}
view.setOptions(height, columns, selectionLimit, callbackContext);
view.load();
cordova.getActivity().addContentView(view,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
});
}
public static class MediaPickerView extends FrameLayout implements StateController {
private GridView grid;
private View balanceView;
private int selectionLimit;
private CallbackContext callbackContext;
private TreeMap<Integer, PictureInfo> selection = new TreeMap();
public MediaPickerView(Context context) {
super(context);
init();
}
public MediaPickerView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
LayoutInflater.from(getContext()).inflate(getResourceId(getContext(), "layout", "view_media"), this);
grid = (GridView)findViewById(getResourceId(getContext(), "id", "gridView"));
balanceView = findViewById(getResourceId(getContext(), "id", "vBalancer"));
}
public void setOptions(double height, int columns, int selectionLimit, CallbackContext callbackContext) {
this.selectionLimit = selectionLimit;
this.callbackContext = callbackContext;
grid.setNumColumns(columns);
LinearLayout.LayoutParams glp = (LinearLayout.LayoutParams) grid.getLayoutParams();
glp.weight = (float) height;
LinearLayout.LayoutParams blp = (LinearLayout.LayoutParams) balanceView.getLayoutParams();
blp.weight = (float) (1 - height);
requestLayout();
}
public void load() {
final GridAdapter adapter = new GridAdapter(getContext(), this);
grid.setAdapter(adapter);
new Thread() {
#Override
public void run() {
final String[] columns = new String[]{
MediaStore.Video.VideoColumns._ID,
MediaStore.Video.VideoColumns.DATA,
MediaStore.Video.VideoColumns.BUCKET_DISPLAY_NAME,
MediaStore.Video.VideoColumns.DISPLAY_NAME,
MediaStore.Video.VideoColumns.DATE_TAKEN,
MediaStore.Video.VideoColumns.MIME_TYPE,
MediaStore.Files.FileColumns.MEDIA_TYPE};
final String orderBy = MediaStore.Video.VideoColumns.DATE_TAKEN
+ " DESC";
final String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO;
Uri queryUri = MediaStore.Files.getContentUri("external");
final Cursor cursor = getContext().getContentResolver().query(
queryUri,
columns,
selection, // Which rows to return (all rows)
null, // Selection arguments (none)
orderBy);
if (cursor.moveToFirst()) {
adapter.setCursor(cursor);
}
}
}.start();
}
}
public static class MediaCache extends LruCache<String, Bitmap> {
public MediaCache(int maxSize) {
super(maxSize);
}
#Override
protected int sizeOf(String key, Bitmap value) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return value.getAllocationByteCount();
}
return value.getByteCount();
}
}
public static class GridAdapter extends BaseAdapter implements AsyncPictureLoader{
private Cursor cursor;
private Executor executor = Executors.newFixedThreadPool(4);
private MediaCache pictureCache;
private int dataColumn;
private StateController stateController;
private ArrayList<PictureView> createdViews = new ArrayList<PictureView>();
public GridAdapter(Context context, StateController stateController) {
this.stateController = stateController;
int memClass = ( (ActivityManager)context.getSystemService( Context.ACTIVITY_SERVICE ) ).getMemoryClass();
int cacheSize = 1024 * 1024 * memClass / 10;
pictureCache = new MediaCache(cacheSize);
}
#Override
public int getCount() {
return cursor != null ? cursor.getCount() : 0;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
PictureView view;
if (convertView != null) {
view = (PictureView) convertView;
} else {
view = new PictureView(pictureCache, parent.getContext(), this, stateController);
createdViews.add(view);
}
view.load(position);
return view;
}
public void setCursor(Cursor cursor) {
this.cursor = cursor;
dataColumn = cursor
.getColumnIndex(MediaStore.Video.VideoColumns.DATA);
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
});
}
#Override
public void loadThumbnail(final PictureInfo pictureInfo, final AsyncPictureLoaderCallback callback) {
if (cursor == null) {
return;
}
executor.execute(new Runnable() {
#Override
public void run() {
if (cursor == null || pictureInfo.cancelled) {
return;
}
synchronized (cursor) {
cursor.moveToPosition(pictureInfo.position);
pictureInfo.uri = cursor.getString(dataColumn);
}
if (pictureInfo.uri == null) {
return;
}
synchronized (pictureCache) {
Bitmap cachedBitmap = pictureCache.get(pictureInfo.uri);
if (cachedBitmap != null) {
pictureInfo.thumbnail = cachedBitmap;
callback.onLoad(pictureInfo);
return;
}
}
int thumbSideSize = callback.getThumbnailSideSize();
if (thumbSideSize <= 0) {
thumbSideSize = 128;
}
// the next 4 variables are needed for videos, see https://stackoverflow.com/a/29555484/7987987
int type = cursor.getColumnIndex(MediaStore.Files.FileColumns.MEDIA_TYPE);
int tInt = cursor.getInt(type);
int colId = cursor.getColumnIndex(MediaStore.Video.VideoColumns._ID);
int id = cursor.getInt(colId);
pictureInfo.thumbnail = decodeSampledBitmapFromUri(pictureInfo.uri, thumbSideSize, thumbSideSize, tInt, id, cordova.getActivity());
if (pictureInfo.thumbnail != null) {
callback.onLoad(pictureInfo);
synchronized (pictureCache) {
pictureCache.put(pictureInfo.uri, pictureInfo.thumbnail);
}
} else {
}
}
});
}
private Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight, int typeInt, int id, Context context) {
Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
if (typeInt == 3) {
// this is a video, handle according to https://stackoverflow.com/a/29555484/7987987
// using BitmapFactory options as seen in the link above
options.inSampleSize = 4; // hardcoded for now until this works, then I'll make it dynamic
options.inPurgeable = true;
bm = MediaStore.Video.Thumbnails.getThumbnail(
context.getContentResolver(), id,
MediaStore.Video.Thumbnails.MINI_KIND, options);
} else {
// this is an image
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);
}
return bm;
}
public int calculateSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
}
public interface AsyncPictureLoader {
void loadThumbnail(PictureInfo position, AsyncPictureLoaderCallback callback);
}
public interface AsyncPictureLoaderCallback {
void onLoad(PictureInfo picture);
int getThumbnailSideSize();
}
public interface StateController {
// stuff here that controls selection
}
public static class PictureInfo {
public int position;
public String uri;
public Bitmap thumbnail;
public volatile boolean cancelled = false;
}
public static class PictureView extends FrameLayout implements AsyncPictureLoaderCallback, CompoundButton.OnCheckedChangeListener {
private final AsyncPictureLoader loader;
private final CheckBox checkBox;
private final StateController stateController;
private final MediaCache pictureCache;
private ImageView vImage;
private PictureInfo pictureInfo;
public PictureView(MediaCache pictureCache, Context context, AsyncPictureLoader loader, StateController stateController) {
super(context);
this.pictureCache = pictureCache;
this.loader = loader;
this.stateController = stateController;
LayoutInflater.from(getContext()).inflate(getResourceId(getContext(), "layout", "view_media_item"), this);
vImage = (ImageView)findViewById(getResourceId(getContext(), "id", "vImage"));
checkBox = (CheckBox)findViewById(getResourceId(getContext(), "id", "checkBox"));
}
public void load(int position) {
if (pictureInfo != null) {
pictureInfo.cancelled = true;
}
pictureInfo = new PictureInfo();
pictureInfo.position = position;
pictureInfo.thumbnail = null;
pictureInfo.uri = null;
vImage.setImageResource(0);
vImage.setVisibility(INVISIBLE);
loader.loadThumbnail(pictureInfo, this);
updateSelection();
}
#Override
public void onLoad(PictureInfo picture) {
if (this.pictureInfo != picture) {
return;
}
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
vImage.setImageBitmap(pictureInfo.thumbnail);
vImage.setVisibility(VISIBLE);
}
});
}
}
}
I'm loading the Album-Artworks in my Music-App. Because I couldn't load them on Main-Thread, I'm using Threads, and they get muddled up!
Sometimes the Image isn't loaded in the correct size or it is shown as a brown square. These issues appear if I scroll fast. If I scroll slow, it works!
The important methods of my MusicStore-Class:
public Bitmap getAlbumArtwork(long AlbumID, int Height, int Width) {
ParcelFileDescriptor pfd;
Bitmap bCover = null;
BitmapFactory.Options bOptions = new BitmapFactory.Options();
bOptions.inJustDecodeBounds = true;
try {
Uri ArtworkUri = Uri.parse("content://media/external/audio/albumart");
Uri uri = ContentUris.withAppendedId(ArtworkUri, AlbumID);
pfd = mContext.getContentResolver().openFileDescriptor(uri, "r");
if (pfd != null) {
BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor(), null, bOptions);
bOptions.inSampleSize = calculateInSampleSize(bOptions, Width, Height);
bOptions.inJustDecodeBounds = false;
bCover = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor(), null, bOptions);
pfd.close();
}
}
catch (IOException ioe) {
BitmapFactory.decodeResource(mContext.getResources(), R.drawable.standardartwork, bOptions);
bOptions.inSampleSize = calculateInSampleSize(bOptions, Width, Height);
bOptions.inJustDecodeBounds = false;
bCover = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.standardartwork, bOptions);
}
return bCover;
}
public void setAlbumArtwork(final long AlbumID, final ImageView ArtworkView) {
Thread thArtwork = new Thread(new Runnable() {
#Override
public void run() {
final Bitmap bArtwork = getAlbumArtwork(AlbumID, ArtworkView.getHeight() / 2, ArtworkView.getWidth() / 2);
handler.postDelayed(new Runnable() {
#Override
public void run() {
ArtworkView.setImageBitmap(bArtwork);
threadList.remove(Thread.currentThread());
}
}, 50);
}
});
threadList.add(thArtwork);
thArtwork.start();
}
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int size = 1;
if (height > reqHeight && width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / size) > reqHeight && (halfWidth / size) > reqWidth) {
size *= 2;
}
}
return size;
}
And my RecyclerViewAdapter:
public class SongRecyclerViewAdapter extends RecyclerView.Adapter<SongRecyclerViewAdapter.Holder> {
private Context mContext;
private Song[] sSongs;
private MusicStore musicStore;
public SongRecyclerViewAdapter(Context context, Song[] songs) {
mContext = context;
sSongs = songs;
musicStore = new MusicStore(mContext);
}
#Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_songview, parent, false);
Holder holder = new Holder(view);
return holder;
}
#Override
public void onBindViewHolder(Holder holder, int position) {
musicStore.setAlbumArtwork(sSongs[position].getAlbumID(), holder.imvSong);
holder.txvSongTitle.setText(sSongs[position].getTitle());
holder.txvSongInfo.setText(sSongs[position].getArtists());
}
#Override
public void onViewDetachedFromWindow(Holder holder) {
}
#Override
public int getItemCount() {
return sSongs != null ? sSongs.length : 0;
}
public class Holder extends RecyclerView.ViewHolder {
LinearLayout linearLayout;
ImageView imvSong;
TextView txvSongTitle;
TextView txvSongInfo;
public Holder(View layout) {
super(layout);
linearLayout = (LinearLayout) layout;
imvSong = (ImageView) layout.findViewById(R.id.imvSong);
txvSongTitle = (TextView) layout.findViewById(R.id.adap_txvSongtitle);
txvSongInfo = (TextView) layout.findViewById(R.id.adap_txvSongInfo);
}
}
}
I'm absolutely open for any other idead to load the Bitmaps correctly!
You could try it out if you want:
https://play.google.com/store/apps/details?id=at.guger.musixs
Thanks!
http://square.github.io/picasso/
The setup and docs are quite simple. You can also passing in the Uri and Picasso will resolve it.
Example
Picasso.with(this).load(uri).into(imageView);
If you can also specify things like width and height, placeholder, and much more.
.resize(width, height).placeholder(placeholderImg)
I was having a problem where my bitmaps were causing my application to skip frames at a rapid rate. I have looked into android's "Displaying Bitmap's Efficiently" documentation, and have implemented some into my code.
I am pretty sure the image is loading correctly, but its not showing in my application. The height and width of the image view is 0 after I do this decoding, so this might be one of the reasons, but I can't change the size of the ImageView for some reason.
BitmapWorkerTask.java:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
private Context context;
public BitmapWorkerTask(ImageView imageView, Context context) {
this.context = context;
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
System.out.println("T" + buildBitmap(data, 100, 100));
return buildBitmap(data, 100, 100);
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask =
AsyncDrawable.getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
public Bitmap buildBitmap(int resId, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(context.getResources(), resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(context.getResources(), resId, options);
}
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int width = options.outWidth;
final int height = options.outHeight;
int inSampleSize = 1;
if(height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
public static boolean cancelPotentialWork(int data, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
final int bitmapData = bitmapWorkerTask.data;
// If bitmapData is not yet set or it differs from the new data
if (bitmapData == 0 || bitmapData != data) {
// Cancel previous task
bitmapWorkerTask.cancel(true);
} else {
// The same work is already in progress
return false;
}
}
// No task associated with the ImageView, or an existing task was cancelled
return true;
}
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
}
}
I am creating the image in the constructor of my View class, and then drawing the TextView in the onDraw method.
public Game(Context context) {
loadBitmap(R.drawable.logo, scoreHolderView);
}
#Override
protected void onDraw(Canvas canvas) {
if(STATE == State.Playing) {
synchronized(spawner.activeTiles) {
for(Iterator<Tile> iter = spawner.activeTiles.iterator(); iter.hasNext();) {
Tile tile = iter.next();
canvas.drawRect(tile, tile.getColor());
}
}
//canvas.drawBitmap(scoreholder, null, scoreIcon, null);
canvas.drawText(String.valueOf(SCORE), 50, 50, blackText);
scoreHolderView.draw(canvas);
}
}
Is the position and size of the ImageView just not being set for some reason? Or are there other problems with my code?
EDIT:
public void loadBitmap(int resId, ImageView imageView) {
if (AsyncDrawable.cancelPotentialWork(resId, imageView)) {
final BitmapWorkerTask task = new BitmapWorkerTask(imageView, getContext());
final AsyncDrawable asyncDrawable =
new AsyncDrawable(getResources(), logo, task);
imageView.setImageDrawable(asyncDrawable);
task.execute(resId);
}
}
I have a CountDownTimer which calls the ChangeWallpaper() method every 15 seconds. The Wallpaper changes as it should but when I try and open the App, it makes the App Drawer screen unresponsive for few seconds. When the App finally opens, everything that I select takes 5-10 seconds to respond. I read about AsyncTask in Android Developer which is supposed to load the Bitmaps outside the UI thread and prevent the App from hanging, but it doesn't seem to be working.
The following code is inside my Activity class:
/** changeWallpaper() **/ - called by CountDownTimer every 15 seconds
protected void changeWallpaper() throws IOException {
Integer totalimages = finallist.size();
if (lastloopcount == totalimages) { // if end of the list of images is reached, it resets and goes back to top.
loopcount = 0;
lastloopcount = 0;
}
for (String imagepath : finallist) { // "finallist" is global variable with all the image's paths in an array list. The Loop count is to select the next image in the array list every 15 seconds.
loopcount++;
if (loopcount > lastloopcount) {
lastloopcount = loopcount;
loopcount = 0;
WallpaperManager wm = WallpaperManager.getInstance(this);
wm.setBitmap(decodeImageFromPath(imagepath));
break;
}
}
}
/** AsyncTask Wallpaper Load **/
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
public BitmapWorkerTask(ImageView imageView) {
new WeakReference<ImageView>(imageView);
}
#Override
protected Bitmap doInBackground(Integer... params) {
return null;
}
}
/** decodeImageFromPath() **/
public Bitmap decodeImageFromPath(String imagepath) {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels << 2;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagepath, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, width, height);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(imagepath, options);
}
/** WallpaperManager (Method) **/
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// ... Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int stretch_width = Math.round((float)width / (float)reqWidth);
int stretch_height = Math.round((float)height / (float)reqHeight);
if (stretch_width <= stretch_height) return stretch_height;
else return stretch_width;
}
Have I used the AsyncTask function correctly?
Is there an easier way to write this?
Thanks in advance.
EDIT:
/** Spinner **/
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String chosenTime = parent.getItemAtPosition(position).toString();
int chosenTimeNew = 0;
if (chosenTime.contains("sec")) {
chosenTime = chosenTime.replace(" sec","");
chosenTimeNew = Integer.parseInt(chosenTime) * 500;
} else if (chosenTime.contains("min") ) {
chosenTime = chosenTime.replace(" min","");
chosenTimeNew = Integer.parseInt(chosenTime) * 30000;
} else if (chosenTime.contains("hour")) {
chosenTime = chosenTime.replace(" hour","");
chosenTimeNew = (Integer.parseInt(chosenTime) * 30000) * 60;
} else if (chosenTime.contains("day")) {
chosenTime = chosenTime.replace(" day","");
chosenTimeNew = ((Integer.parseInt(chosenTime) * 30000) * 60) * 24;
}
rSpeed = chosenTimeNew;
}
EDIT 2:
Called by CountDownTimer():
new BitmapWorkerTask(null).execute(imagepath);
Then:
/** AsyncTask Wallpaper Load **/
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
public BitmapWorkerTask(ImageView imageView) {
new WeakReference<ImageView>(imageView);
}
#Override
protected Bitmap doInBackground(String... params) {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels << 2;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(params[0], options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, width, height);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeFile(params[0], options);
return bmp;
}
protected void onPostExecute(Bitmap bmp) {
Context context = getApplicationContext();
WallpaperManager wm = WallpaperManager.getInstance(context);
try {
wm.setBitmap(bmp);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/** WallpaperManager (Method) **/
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// ... Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int stretch_width = Math.round((float)width / (float)reqWidth);
int stretch_height = Math.round((float)height / (float)reqHeight);
if (stretch_width <= stretch_height) return stretch_height;
else return stretch_width;
}
Your main code(processing the bitmap) should be called from within the doInBackground method. Else it's the same as a synchronous call here.
#Override
protected Bitmap doInBackground(String... params) {
Bitmap bmp = decodeImageFromPath(params[0]);
return bmp;
}
protected void onPostExecute(Bitmap bmp) {
wm.setBitmap(bmp)
}
new BitmapWorkerTask ().execute(imagePath);
http://developer.android.com/reference/android/os/AsyncTask.html
You can refer to the example on this link.
You don't even use your BitmapWorkerTask! There's no magic that does arbitrary parts of your code in background just when you write some AsyncTaskinto your code. You've got to use it too, you know.
Move the long lasting parts of your codes into the doInBackground() method of the AsyncTask and invoke it like so: new BitmapWorkerTask().execute();
EDIT
To pass the image-path, change the definition of your BitmapWorkerTask like to something like this ... extends AsyncTask<String, Void, Bitmap> ... (note the String instead of the Integer), and pass the image-path as parameter to the execute() method.
new BitmapWorkerTask().execute(imagePath);
Be aware that now this runs asynchronously, so the execute() call returns immediately, but the loading of the image will take some time still.
Also read the Painless Threading article.
I am trying to change the image during runtime in livewallpaper. I am calling method changeFlowers. I able to print Log.W() but unable to change bitmap. Thanks in Advance.
When I call from here f1.changeColor(this._theme) it is not working, I want to change the fish bitmap image during runtime
public void changeFlowers(int i)
{
FlowerOne f1 = (FlowerOne)(this._fishes.get(i));
f1.changeColor(this._theme); //
}
changeColor function from FishOne.java
public void changeColor(int mfishColor)
{
switch(mfishColor) {
case 1:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
this.leftBitmap = BitmapFactory.decodeResource(getContext().getResources(), com.gotit.livewallpaper.fishshd.R.drawable.fish1, options);
BitmapFactory.Options options1 = new BitmapFactory.Options();
options1.inPurgeable = true;
this.rightBitmap = BitmapFactory.decodeResource(getContext().getResources(), com.gotit.livewallpaper.fishshd.R.drawable.fish2, options1);
break;
case 2:
BitmapFactory.Options options2 = new BitmapFactory.Options();
options2.inPurgeable = true;
this.leftBitmap = BitmapFactory.decodeResource(getContext().getResources(), com.gotit.livewallpaper.fishshd.R.drawable.redfish1, options2);
BitmapFactory.Options options3 = new BitmapFactory.Options();
options3.inPurgeable = true;
this.rightBitmap = BitmapFactory.decodeResource(getContext().getResources(), com.gotit.livewallpaper.fishshd.R.drawable.redfish2, options3);
break;
default:
}
}
Aquarium.java
public class Aquarium {
private AquariumThread _aquariumThread;
private SurfaceHolder _surfaceHolder;
private ArrayList<Renderable> _fishes;
private Bitmap _backgroundImage;
private Context _context;
public void render(){
Canvas canvas = null;
try{
canvas = this._surfaceHolder.lockCanvas(null);
synchronized (this._surfaceHolder) {
this.onDraw(canvas);
}
}finally{
if(canvas != null){
this._surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
protected void onDraw(Canvas canvas) {
this.renderBackGround(canvas);
for (Renderable renderable : this._fishes) {
renderable.render(canvas);
}
};
public void initialize(Context context, SurfaceHolder surfaceHolder) {
this._fishes = new ArrayList<Renderable>();
}
protected void onDraw(Canvas canvas) {
this.renderBackGround(canvas);
for (Renderable renderable : this._fishes) {
renderable.render(canvas);
}
};
public void start(){
this._aquariumThread.switchOn();
}
public void stop(){
boolean retry = true;
this._aquariumThread.switchOff();
while (retry) {
try {
this._aquariumThread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
public synchronized void addFishes() {
Point startPoint1 = new Point(1,300);
this._fishes.add(new fishOne(this._context, this, startPoint1, 90));
Point startPoint2 = new Point(100,300);
this._fishes.add(new fishOne(this._context, this, startPoint2, 90));
Point startPoint3 = new Point(200,300);
this._fishes.add(new fishOne(this._context, this, startPoint3, 90));
}
public void changeFlowers(int i)
{
FlowerOne f1 = (FlowerOne)(this._fishes.get(i));
f1.changeColor(this._theme);
}
}
FishOne.java
public class fishOne extends Animal {
private static final int TOTAL_FRAMES_IN_SPRITE = 1;
private static final int CLOWN_FISH_FPS = 1;
int _fishColor;
Bitmap leftBitmap;
Bitmap rightBitmap;
public fishOne(Context context, Aquarium aquarium, Point startPoint, int speed){
super(context, aquarium);
this.changeColor(1);
this.initialize(this.leftBitmap, this.rightBitmap, CLOWN_FISH_FPS, TOTAL_FRAMES_IN_SPRITE, startPoint, speed);
}
public void changeColor(int mfishColor)
{
switch(mfishColor) {
case 1:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
this.leftBitmap = BitmapFactory.decodeResource(getContext().getResources(), com.gotit.livewallpaper.fishshd.R.drawable.fish1, options);
BitmapFactory.Options options1 = new BitmapFactory.Options();
options1.inPurgeable = true;
this.rightBitmap = BitmapFactory.decodeResource(getContext().getResources(), com.gotit.livewallpaper.fishshd.R.drawable.fish2, options1);
break;
case 2:
BitmapFactory.Options options2 = new BitmapFactory.Options();
options2.inPurgeable = true;
this.leftBitmap = BitmapFactory.decodeResource(getContext().getResources(), com.gotit.livewallpaper.fishshd.R.drawable.redfish1, options2);
BitmapFactory.Options options3 = new BitmapFactory.Options();
options3.inPurgeable = true;
this.rightBitmap = BitmapFactory.decodeResource(getContext().getResources(), com.gotit.livewallpaper.fishshd.R.drawable.redfish2, options3);
break;
default:
}
}
public void render(Canvas canvas){
super.render(canvas);
}
}
Re-initializing again inside the method. it worked
this.initialize(this.leftBitmap, this.rightBitmap, CLOWN_FISH_FPS, TOTAL_FRAMES_IN_SPRITE, startPoint, speed);