What I'm trying to do:
I have several JPG files saved in a subfolder under the "assets" folder. My app should go into this subfolder, load the first file, display it for 30 seconds, then load the second file, display it for 30 seconds, and so on, ..., until the last file. This is done in a loop.
What problem I'm having:
The app goes through all the iterations of the loop. It loads each file correctly, reports the width and height of each image correctly. However, the screen is black with no images being displayed, until after the whole loop finishes. Then the last image is displayed.
Here is my layout.xml file (I'd like to handle images of different sizes, but will worry about it later):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mylayout_id"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ImageView
android:id="#+id/image_display"
android:layout_width="400dp"
android:layout_height="400dp" >
</ImageView>
<Button
android:id="#+id/button_start"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="pressedStart"
android:text="#string/btn_start" />
</LinearLayout>
Here is my code:
public class MyImageActivity extends Activity {
private final String TAG = "MyImageActivity";
private ImageView mIV;
private Bitmap mFaceBitmap;
private AssetManager mAssetMan = null;
private String[] mImgFileNames = null;
private int mBitmapWidth;
private int mBitmapHeight;
#Override
public void onCreate(Bundle savedInstanceState) {
Log.e(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
mIV = (ImageView) findViewById(R.id.image_display);
}
#Override
protected void onResume() {
Log.e(TAG, "onResume");
super.onResume();
mAssetMan = getAssets();
}
public void pressedStart(View view) {
Log.e(TAG, "pressedStart");
try {
mImgFileNames = mAssetMan.list("my_subfolder");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Log.d(TAG, "mImgFileNames = " + mImgFileNames);
Log.d(TAG, "number of image files = " + mImgFileNames.length);
for (int i = 0; i < mImgFileNames.length; i++) {
Log.d(TAG, "image file name = " + mImgFileNames[i]);
mIV.setImageDrawable(null);
mIV.invalidate();
InputStream istr = null;
try {
istr = mAssetMan.open("my_subfolder" + mImgFileNames[i]);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// load the photo
Bitmap b = BitmapFactory.decodeStream(istr);
mFaceBitmap = b.copy(Bitmap.Config.RGB_565, true);
b.recycle();
mBitmapWidth = mFaceBitmap.getWidth();
mBitmapHeight = mFaceBitmap.getHeight();
Log.d(TAG, "mBitmapWidth = " + mBitmapWidth);
Log.d(TAG, "mBitmapHeight = " + mBitmapHeight);
Drawable drawable = new BitmapDrawable(getResources(), mFaceBitmap);
mIV.setImageDrawable(drawable);
mIV.invalidate();
SystemClock.sleep(5000);
}
}
}
I found some other discussions about similar problems here and here. I tried a few suggested solutions, e.g. adding setImageDrawable(null) to force ImageView to update; that did not work for me.
Anything wrong with my approach? I really appreciate your helps and suggestions.
Thanks in advance & Have a nice weekend.
This is happening because you are placing the new image within the holder and calling invalidate to make it display but then you turn around and put the UI thread to sleep for 5 seconds so I cannot update it. By the time the system goes to update it, it is already moving on to the next image. I would use a AsyncTask to load the next Image in the background and then put a timer of 5 seconds in the the background thread before it updates the UI with the new data.
EDIT
Here is the reworked version of your code that is operational if this is your exact code. If not you should be fine. (if the formatting is bad, it is because my work uses IE8 still and it doesn't let me see the formatting. I will fix when I get home.)
public class MyImageActivity extends Activity {
private final String TAG = "MyImageActivity";
private ImageView mIV;
#Override
public void onCreate(Bundle savedInstanceState) {
Log.e(TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
mIV = (ImageView) findViewById(R.id.image_display);
}
#Override
protected void onResume() {
Log.e(TAG, "onResume");
super.onResume();
}
public void pressedStart(View view) {
new SlideShowTask().execute();
}
private class SlideShowTask extends AsyncTask<Void, Drawable, Void>{
private Bitmap mFaceBitmap;
private AssetManager mAssetMan = null;
private String[] mImgFileNames = null;
private int mBitmapWidth;
private int mBitmapHeight;
#Override
Protected Void doInBackground(Void... params){
mAssetMan = getAssets();
Log.e(TAG, "pressedStart");
try {
mImgFileNames = mAssetMan.list("my_subfolder");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Log.d(TAG, "mImgFileNames = " + mImgFileNames);
Log.d(TAG, "number of image files = " + mImgFileNames.length);
for (int i = 0; i < mImgFileNames.length; i++) {
Log.d(TAG, "image file name = " + mImgFileNames[i]);
InputStream istr = null;
try {
istr = mAssetMan.open("my_subfolder/" + mImgFileNames[i]);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// load the photo
Bitmap b = BitmapFactory.decodeStream(istr);
mFaceBitmap = b.copy(Bitmap.Config.RGB_565, true);
b.recycle();
mBitmapWidth = mFaceBitmap.getWidth();
mBitmapHeight = mFaceBitmap.getHeight();
Log.d(TAG, "mBitmapWidth = " + mBitmapWidth);
Log.d(TAG, "mBitmapHeight = " + mBitmapHeight);
Drawable drawable = new BitmapDrawable(getResources(), mFaceBitmap);
publishProgress(drawable);
SystemClock.sleep(5000);
}
}
#Override
Protected Void onProgressUpdate(Drawable... values){
super.onProgressUpdate(values);
mIV.setImageDrawable(values[0]);
mIV.invalidate();
}
}
}
This happens because you put the main Ui Thread to sleep. And you should never do that. As it means that the whole application will hang for 5seconds which is wrong.
You should create a new thread and update the UI from there like this code
which is the easiest.
runOnUiThread(new Runnable(){
#Override
public void run(){
<--Your Code Here -->
<--sleep for 5000ms-->
}
});
or use a Handler or an ASynkTask.
There are many good tutorials for each of them here.
Try using a new thread or Runnable and add your code to the run function and than make that thread sleep.
Since you are updating your UI you would also need a handler. check the link below
www.vogella.com/articles/AndroidBackgroundProcessing/article.html
Related
Hi guys trying to implement AsyncTask here in my project, there seems to be no error shown by Android Studio either and have debugged to see if the bitmaps are downloaded and yes it is. I dont know what the problem is so here is my code.
My Activity:
public class MainActivity extends AppCompatActivity {
public ImageView imageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView =findViewById(R.id.justAnImage);
String[] strings = new String[2];
strings[0] = "blah.com/sds.jpg";
strings[1] = "blah.com/sds2.jpg";
new AsyncDownloader(this,imageView).execute(strings);
}
public void StartAnimation(ImageView imageView, Bitmap[] bitmaps)
{
AnimationDrawable animation = new AnimationDrawable();
for (int i=0;i<bitmaps.length;i++) //replaced erroneous code-> (int i=0;i>=bitmaps.length;i++)
{
animation.addFrame(new BitmapDrawable(getApplicationContext().getResources(),bitmaps[i]),1000);
}
animation.setOneShot(false);
imageView.setBackground(animation);
// start the animation!
animation.start();
}
}
My Async downloader class
public class AsyncDownloader extends AsyncTask<String[],Void,Bitmap[]> {
private Bitmap[] bitmapArray;
private ImageView imageView;
MainActivity mainActivity;
public AsyncDownloader(MainActivity mainActivity,ImageView imageView) {
this.imageView = imageView;
bitmapArray = new Bitmap[2];
this.mainActivity = mainActivity;
}
#Override
protected Bitmap[] doInBackground(String[]... strings) {
for(int i=1;i>=0;i--)
{
try {
URL url = new URL(strings[0][i]);
bitmapArray[i] = BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch (Exception e) {
Log.e("DOWNLOAD ASYNC", e.getMessage());
}
}
return bitmapArray;
}
#Override
protected void onPostExecute(Bitmap[] bitmapArray) {
super.onPostExecute(bitmapArray);
//imageView.setImageBitmap(bitmapArray[0]);
mainActivity.StartAnimation(imageView,bitmapArray);
}
}
I cant find the problem here, debugging is so terrible in Android Studio. If any one could help, i shall be thankful.
EDIT 1: After suggestions from fellow stack guys, it is clear that the animation drawable is not working as desired and nonetheless there is no image showing up on the ImageView control.
EDIT 2: Code works after correcting the loop specified in the answer. Found an alternative to display images after certain interval. The code goes like this :
public void StartAnimation(final ImageView imageView, final Bitmap[] bitmaps)
{
handler = new Handler();
Runnable runnable = new Runnable() {
int i = 0;
public void run() {
imageView.setImageBitmap(bitmaps[i]);
i++;
if (i > bitmaps.length - 1) {
i = 0;
}
handler.postDelayed(this, 1200);
}
};
handler.postDelayed(runnable, 1200);
}
Your code is perfect, except loop condition inside method StartAnimation.
issue is with loop condition which is always false.
To fix the issue change
From
for (int i = 0; i >= bitmaps.length; i++)
To
for (int i = 0; i < bitmaps.length; i++)
use fresco or picosso or glide library
these libraries automatically caching and managing memory.
it's easy to work with picosso and glide but fresco adds more features to developers.
So Im having trouble using Microsoft's Emotion API for Android. I have no issues with regards to running the Face API; Im able to get the face rectangles but I am not able to get it working on the emotion api. I am taking images using the builtin Android camera itself. Here is the code I am using:
private void detectAndFrame(final Bitmap imageBitmap)
{
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
imageBitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
ByteArrayInputStream inputStream =
new ByteArrayInputStream(outputStream.toByteArray());
AsyncTask<InputStream, String, List<RecognizeResult>> detectTask =
new AsyncTask<InputStream, String, List<RecognizeResult>>() {
#Override
protected List<RecognizeResult> doInBackground(InputStream... params) {
try {
Log.e("i","Detecting...");
faces = faceServiceClient.detect(
params[0],
true, // returnFaceId
false, // returnFaceLandmarks
null // returnFaceAttributes: a string like "age, gender"
);
if (faces == null)
{
Log.e("i","Detection Finished. Nothing detected");
return null;
}
Log.e("i",
String.format("Detection Finished. %d face(s) detected",
faces.length));
ImageView imageView = (ImageView)findViewById(R.id.imageView);
InputStream stream = params[0];
com.microsoft.projectoxford.emotion.contract.FaceRectangle[] rects = new com.microsoft.projectoxford.emotion.contract.FaceRectangle[faces.length];
for (int i = 0; i < faces.length; i++) {
com.microsoft.projectoxford.face.contract.FaceRectangle rect = faces[i].faceRectangle;
rects[i] = new com.microsoft.projectoxford.emotion.contract.FaceRectangle(rect.left, rect.top, rect.width, rect.height);
}
List<RecognizeResult> result;
result = client.recognizeImage(stream, rects);
return result;
} catch (Exception e) {
Log.e("e", e.getMessage());
Log.e("e", "Detection failed");
return null;
}
}
#Override
protected void onPreExecute() {
//TODO: show progress dialog
}
#Override
protected void onProgressUpdate(String... progress) {
//TODO: update progress
}
#Override
protected void onPostExecute(List<RecognizeResult> result) {
ImageView imageView = (ImageView)findViewById(R.id.imageView);
imageView.setImageBitmap(drawFaceRectanglesOnBitmap(imageBitmap, faces));
MediaStore.Images.Media.insertImage(getContentResolver(), imageBitmap, "AnImage" ,"Another image");
if (result == null) return;
for (RecognizeResult res: result) {
Scores scores = res.scores;
Log.e("Anger: ", ((Double)scores.anger).toString());
Log.e("Neutral: ", ((Double)scores.neutral).toString());
Log.e("Happy: ", ((Double)scores.happiness).toString());
}
}
};
detectTask.execute(inputStream);
}
I keep getting the error Post Request 400, indicating some sort of issue with the JSON or the face rectangles. But I'm not sure where to start debugging this issue.
You're using the stream twice, so the second time around you're already at the end of the stream. So either you can reset the stream, or, simply call the emotion API without rectangles (ie skip the call to the face API.) The emotion API will determine the face rectangles for you.
I'm using a thread to set an image as background and in this thread i have a dialog. The dialog starts and should be close when the wallpaper will be set. This is the code so far
setWallbtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
final ProgressDialog myPd_ring=ProgressDialog.show(SingleWall.this, "Setting wallpaper", "", true);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
WallpaperManager wallManager = WallpaperManager.getInstance(getApplicationContext());
try {
image = BitmapFactory.decodeStream(url.openConnection().getInputStream());
wallManager.setBitmap(image);
Toast.makeText(SingleWall.this, "Wallpaper Set Successfully!!", Toast.LENGTH_SHORT).show();
myPd_ring.dismiss();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(SingleWall.this, "Setting WallPaper Failed!!", Toast.LENGTH_SHORT).show();
myPd_ring.dismiss();
}
}
}, 4000);
}
});
So, on click in a button starts the thread and for 4 seconds the dialog should be visible with the progress icon. But it is not correct! the time to set the background could be more or less than 4 seconds! So the 4000 should be calculates in base of the time to set the image as wallpaper. Is it possible?
ps. I can't use a AsyncTask because i get many NullPointerExceptions
Note that you are not using a separate Thread with the code in your question, you are running a Runnable on the main UI thread.
If you look at the documentation, it's recommended to use an AsyncTask for decoding Bitmaps, and it's also the best way to achieve your desired result, where the ProgressDialog is dismissed only after the process is complete, which can take an unpredictable amount of time.
You just need to put the code in it's correct place, and give it what it needs through the varargs passed in.
Here is how you should start the AsyncTask:
setWallbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new LoadImage().execute(url);
}
});
Then, create the AsyncTask as a sub-class of the SingleWall Activity.
Put the network code in doInBackground() which will download and decode the Bitmap, and then put the UI related code in onPostExecute(), which runs on the UI thread.
Note that you can also use a WeakReference to the WallpaperManager instance, as outlined in the link above, but I'll keep it simple here and just access wallManager directly, which you can do if the AsyncTask is a sub-class of your Activity.
class LoadImage extends AsyncTask<URL, Void, Bitmap> {
ProgressDialog myPd_ring;
#Override
protected void onPreExecute() {
//Start Progress Dialog here
myPd_ring = ProgressDialog.show(SingleWall.this, "Setting wallpaper", "", true);
}
//Runs in a background Thread
#Override
protected Bitmap doInBackground(URL... params) {
URL url = params[0];
Bitmap image = null;
try {
image = BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
return image;
}
//Runs on the UI Thread
#Override
protected void onPostExecute(Bitmap image) {
myPd_ring.dismiss();
if (image == null){
Toast.makeText(SingleWall.this, "Setting WallPaper Failed!!", Toast.LENGTH_LONG).show();
}
else{
//set image here
try {
SingleWall.this.wallManager.setBitmap(image);
Toast.makeText(SingleWall.this, "Wallpaper Set Successfully!!", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(SingleWall.this, "Setting WallPaper Failed!!", Toast.LENGTH_LONG).show();
}
}
}
}
Use the AsyncTask, the null pointers are probably coming because you are trying to update the UI during the task's processing. You might need to use something like this from inside the AsyncTask:
activity.runOnUiThread(new Runnable() {
public void run() {
activity.doSomeSpecialUIWork();
}
});
}
Hope that works - that's what solved it for me when I was getting strange null pointers during an AsyncTask.
Here's an example from another post: how to use runOnUiThread
For your specific code, maybe this:
setWallbtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
activity.runOnUiThread(new Runnable() {
public void run() {
final ProgressDialog myPd_ring=ProgressDialog.show(SingleWall.this, "Setting wallpaper", "", true);
// TODO Auto-generated method stub
WallpaperManager wallManager = WallpaperManager.getInstance(getApplicationContext());
try {
image = BitmapFactory.decodeStream(url.openConnection().getInputStream());
wallManager.setBitmap(image);
Toast.makeText(SingleWall.this, "Wallpaper Set Successfully!!", Toast.LENGTH_SHORT).show();
myPd_ring.dismiss();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(SingleWall.this, "Setting WallPaper Failed!!", Toast.LENGTH_SHORT).show();
myPd_ring.dismiss();
}
}
});
}
}
});
What I want to do: I am putting rows into a ListView using a custom "Location" adapter. I am trying to add a Bitmap into a row of the ListView. This Bitmap is coming from a URL. So, I have a global variable public static Bitmap bitmap and want to update this variable using an AsyncTask. Here is my code:
try {
String s = "";
JSONArray jArray = new JSONArray(result);
for (int i = 0; i < jArray.length(); i++) {
final JSONObject json = jArray.getJSONObject(i);
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
//here I am calling my new task and giving it the ID to find the image
BitmapWorkerTask myTask = new BitmapWorkerTask(json.getInt("ID"));
myTask.execute();
adapter.add(new Location(bitmap, json
.getString("PlaceTitle"), json
.getString("PlaceDetails"), json
.getString("PlaceDistance"), json
.getString("PlaceUpdatedTime")));
bitmap = null;
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
} catch (Exception e) {
// TODO: handle exception
Log.e("log_tag", "Error Parsing Data " + e.toString());
}
and here is my AsyncTask
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private int photoID = 0;
public BitmapWorkerTask(int photoID) {
// Use a WeakReference to ensure the ImageView can be garbage collected
this.photoID = photoID;
}
// Decode image in background.
#Override
protected Bitmap doInBackground(Integer... params) {
String initialURL = "http://afs.spotcontent.com/img/Places/Icons/";
final String updatedURL = initialURL + photoID + ".jpg";
Bitmap bitmap2 = null;
try {
bitmap2 = BitmapFactory.decodeStream((InputStream) new URL(
updatedURL).getContent());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap2;
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap2) {
bitmap = bitmap2;
}
}
So, for each iteration, I am feeding the AsyncTask an ID which it uses to find the image, and then (I was hoping) should update the global Bitmap which is passed into the adapter. When I run my app, the every listing's picture is empty. Any ideas on what I'm doing wrong?
Consider the following possible order your commands get executed (remembering that tasks run in the background, so the order is non-deterministic):
myTask.execute()
BitmapWorkerTask.doInBackground()
adapter.Add(new Location(bitmap, .......
BitmapWorkerTask.onPostExecute()
When you create the Location() object in step 3, the 'bitmap' object being passed is the global object pointer. Its value is NOT valid yet, because onPostExecute() wasn't called yet. So the Location object is created with a non-bitmap object. On step 4, when the bitmap is finally retrieved, the value of the global object pointer is changed (correctly), but that doesn't affect the (empty) bitmap object already passed to Location on step 2... Which is why you don't see the bitmap on your view.
What you could do is pass an additional parameter to the BitmapWorkerTask constructor: You could pass the Location object (or the underlying bitmap). From onPostExecute() you could then update that Location/bitmap object with the retrieved bitmap. You don't need a global variable here.
I have UI component where I have table which consist of ImageViews, Now I want to set the image from URL.
Now when I try to call asynchronously the other class where the image is set to UI it gives me error that UI can be updated only by UI thread. I want to load the UI and load images when they are available.
Here is my code :
Calling function...
new AsyncImageLoader(context,imgviewArray).execute();
// Called class..
#Override
protected Void doInBackground(Void... urls) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
int i = 0;
try{
db=new MyDB(mainact_instance);
System.out.println("Inside doInBackground");
if(isNetworkAvailable())
{
System.out.println("Network connected");
System.out.println("Fetching from network-Images");
Movie[] movies = db.selectRecords();
for(Movie mv : movies)
{
try {
URL url = new URL(IMAGE_URL+mv.getId()+".jpg");
Bitmap bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
images[i].setImageBitmap(bmp);
i++;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
else{
System.out.println("Network not connected..fetching max id row");
}
}catch(Exception ex){ex.printStackTrace();
}
return null;
//db.close();
}
Please tell me how I can I achieve this?
move your UI thread related code to onPostExecute || onProgressUpdate
for example:
protected void onPostExecute(Long result) {
// after finishing fetching your image from URL, update the ImageView here
images[i].setImageBitmap(bmp);
}
I can see you are downloading multiple images inside the AsyncTask then you can use PublishProgress you need to update your AsyncTask definition to allow passing Bitmaps to publishProgress method as follow:
AsyncTask<whatever..., Bitmaps, whatever...>()
Then
publishProgress(yourBitmapObject);
protected void onProgressUpdate(Bitmap[] values) {
images[i].setImageBitmap(values[0]);
};