I have to manipulate pixels of a bitmap every one second, during another operation, and show the result in ImageView. This is a method that manipulates bitmap and updates ImageView:
private void overlay(Bitmap bitmap) {
int j = 70;
while (flag) {
//here I manipulate some pixels of bitmap that corresponds to "j"
//and save result in "tempBmp":
...
//update imageView:
iv1.setImageBitmap(tempBmp);
iv1.postInvalidate();
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// }
//update "j":
j = (j+1)%src.rows();
}
}
Above method is called from UI thread (onclick event of button) in this way:
runOnUiThread(new Runnable() {
public void run() {
overlay(bmp);
}
});
When App is running, there is no problem, App crash or ANR, but the ImageView (iv1) does not update. I debug the code and bitmap's pixels are manipulating. Also I uncomment try/catch block, but the result was the same.
It is strange: when I comment start and end of while block, bitmap manipulate correctly (one time and for j = 70) and the ImageView updates as expected!
Update question:
With respect to comments and answers of eshayne and Onik I changed pixel manipulation in overlay method to a simple operation and check the result, but result was the same. Real overlay method use openCV but here I change color of pixels, so you can see result without using openCV:
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
new Thread(new Runnable() {
#Override
public void run() {
overlay(bmp);
}
}).run();
}
});
overlay method,simplified:
private void overlay(Bitmap bitmap){
int j = 0;
tempBmp = Bitmap.createBitmap(bitmap);
while (j < bitmap.getHeight()) {
for (int i = 0; i < bitmap.getWidth(); i++) {
tempBmp.setPixel(i, j, 0);
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// post the changes to Ui thread
iv1.post(new Runnable() {
#Override
public void run() {
iv1.setImageBitmap(tempBmp);
}
});
j = (j + 1);
}
}
How can I update ImageView, every second with while? Did I do any thing wrong?
Try moving your loop outside of the UI thread, then just run the setImageBitmap call within the UI thread. Something like:
// from non-UI thread:
while (flag) {
// perform image manipulation
runOnUiThread(new Runnable() {
#Override
public void run() {
iv1.setImageBitmap(tempBmp);
}
});
}
Related
i have imageView
<ImageView
android:id="#+id/loading"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerCrop"
android:layout_gravity="center_horizontal"
android:src="#drawable/spinner"/>
and image is
i want create an animation which change position of image inside imageView
i tried this
android:scrollX="-80dp"
this in xml and static i want animation
You can do it by creating a thread and a handler where the thread sleeps for a certain amount of time and informs handler by sending message when it wakes up and handler will update the image in image view.
Create 4 Fields like below :
private ImageView mImageView;
private Handler mImageChangingHandler;
private int mCurrentImageIndex = 0;
private BackgroundThread mImageUpdateThread;
In onCreate() get the ImageView, spilt your bitmaps and add it to a list and create a handler which updates imageview. Here you should know the number of individual bitmaps which is in main Bitmap. Like :
mImageView = (ImageView) findViewById(R.id.img);
Bitmap mainBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.anim);
//number of individual bitmaps, which you should know.
final int numberOfImages = 21;
//individual bitmap width
int bitmapWidth = mainBitmap.getWidth() / numberOfImages;
//individual bitmap height
int bitmapHeight = mainBitmap.getHeight();
//list which holds individual bitmaps.
final List<Bitmap> animBitmaps = new ArrayList<>(numberOfImages);
//split your bitmap in to individual bitmaps
for (int index = 0; index < numberOfImages; index++) {
animBitmaps.add(Bitmap.createBitmap(mainBitmap, index * bitmapWidth, 0, bitmapWidth, bitmapHeight));
}
//set 1st bitmap to imageView.
mImageView.setImageBitmap(animBitmaps.get(mCurrentImageIndex));
mImageChangingHandler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
//increament current bitmap index
mCurrentImageIndex++;
//if current index is greater than the number of images, reset it to 0
if (mCurrentImageIndex > numberOfImages - 1) {
mCurrentImageIndex = 0;
}
mImageView.setImageBitmap(animBitmaps.get(mCurrentImageIndex));
return true;
}
});
//Create the background thread by passing the handler and start.
mImageUpdateThread = new BackgroundThread(mImageChangingHandler);
mImageUpdateThread.setRunning(true);
mImageUpdateThread.start();
And This will be your BackgroundThread.class :
public static class BackgroundThread extends Thread {
private Handler mHandler;
private boolean mRunning;
public BackgroundThread(Handler handler) {
mHandler = handler;
}
public void setRunning(boolean running) {
mRunning = running;
}
#Override
public void run() {
super.run();
while (mRunning) {
try {
//this sleeps for 100 milliseconds
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//send message to handler after sleep
mHandler.sendEmptyMessage(0);
}
}
}
}
Finally in onDestroy() stop the thread. Like :
mImageUpdateThread.setRunning(false);
boolean stopThread = true;
while (stopThread) {
try {
mImageUpdateThread.join();
//thread already stopped
stopThread = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mImageUpdateThread = null;
Note : If you want to increase the update speed, reduce the sleep time in thread.
I am in the process of making a simple practice Morse code application.
I am trying to make an Image View flash between black and white in the SOS sequence based on Morse. When researching how to achieve this i realized i would have to do this on a separate thread in order to not block the UI thread.
The problem is that i am currently updating the Image View from outside of the UI thread which is stated to be a bad idea. At the moment i am trying to pass the Image View to the worker thread class that contains the logic for the screen flash.
I am new to multi-threading and i am pretty sure i am doing this completely backwards/incorrectly. Does anyone have an advice or ideas as of the best method to go about this?
new Thread(new Runnable() {
public void run() {
sf = new ScreenFlash("...---...", imgV);
sf.flashScreen();
}
}).start();
public ScreenFlash(String message, ImageView imgV){
this.message = message.replaceAll(".(?!$)", "$0 ");
this.imgV = imgV;
}
public void flashScreen() {
int offIntervalTime = 50;
char[] cArray = message.toCharArray();
for (int i = 0; i < cArray.length; i++) {
if (cArray[i] == '.') {
imgV.setBackgroundColor(Color.WHITE);
try {
Thread.sleep(50);
}catch(Exception e)
{
}
imgV.setBackgroundColor(Color.BLACK);
try {
Thread.sleep(offIntervalTime);
}catch(Exception e)
{
}
} else if(cArray[i] == ' ')
{
try{
Thread.sleep(100);
}catch(Exception e)
{
}
}
else {
try{
imgV.setBackgroundColor(Color.WHITE);
Thread.sleep(dash);
}catch(Exception e)
{
}
try{
imgV.setBackgroundColor(Color.BLACK);
Thread.sleep(offIntervalTime);
}catch(Exception e)
{
}
}
}
Take a look at the Handler class documentation and how to communicate with the UI thread from a background thread:
http://developer.android.com/training/multiple-threads/communicate-ui.html
You can update your imageView in onHandleMessage()
#Override
public void handleMessage(Message inputMessage) {
imageView.setBackgroundColor(android.R.color.white)
...
}
Suppose that I have 10 text views in my layout. I want to change their background one by one with a small delay between each operation. Here's a sample code:
public void test(){
for (int i=0 ; i < 10 ; i++){
myTextViews[i].setBackgroundResource(R.color.red);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
}
}
The problem is that during the time of running this function main thread blocks and backgrounds don't change. They change all together when the program finishes running the function. What should I do if I want the user to see each background is changed at the correct time and then next one...?
When you call Thread.sleep(), the thread you are actually blocking is the UI thread, which is why you're seeing a hang-up. What you need to do is start up another Thread to handle the sleeps, or delays, that you want, and then utilize the runOnUiThread method
Try this:
public void test() {
new Thread(new Runnable() {
public void run() {
for(int i = 0; i < 10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//anything related to the UI must be done
//on the UI thread, not this thread we just created
runOnUiThread(new Runnable() {
public void run() {
myTextViews[i].setBackgroundResource(R.color.red);
}
});
}
}
}).start();
}
Can I use a thread for increment a counter and shows it in a frame of Android activity.
Public class MainActivity extendsActivity {
TextView counter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
counter = (TextView) findViewById(R.id.TV_counter);
Thread t = new Thread() {
public void run() {
runOnUiThread(new Runnable() {
public void run() {
for (int i = 0; i < 5; i++) {
try {
counter.setText("" + i);
System.out.println("Value of i= " + i);
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
};
t.start();
}
}
I wrote this code, but it run properly in console, but the text view displays i=4 in the terminal, I modified the time to sleep(3000) and the problem persists.
First you don't ever want to put sleep in UI Thread that can lead to unresponsive user interface and that is never good. You should use it just to update your graphics. Try replacing your code with this
Thread t = new Thread() {
public void run() {
for (int i = 0; i < 5; i++) {
try {
final int a = i;
runOnUiThread(new Runnable() {
public void run() {
counter.setText("" + a);
}
});
System.out.println("Value of i= " + i);
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
You are going to notice that sleep and for loop is outside UIThread and in your first thread, so basically all of your math is done outside and you just display the results.
This is just a correction of your code and suggestion for further thinking
EDIT: And for you to better understand why your code is not working, you set some value on your TextView, and immediately after you set UIThread to sleep, UIThread blocks instead of giving it time to finish updating graphics, after he finish sleep you set new value, and he never got to update previous one so in the end you see just the last one.
Hope this helps and enjoy your work.
you can use a CountDownTimer, and update your UI in the onTick() method ( this method is executed on the UI Thread):
int i=0;
CountDownTimer timer = new CountDownTimer(5000,1000) {
#Override
public void onTick(long millisUntilFinished) {
// this method will be executed every second ( 1000 ms : the second parameter in the CountDownTimer constructor)
i++;
txt.setText(i);
}
#Override
public void onFinish() {
// TODO Auto-generated method stub
}
};
timer.start();
What I'm trying to create is a simple progress bar, that would load for ~10 sec.
So what I want is a for loop like this:
for(int i = 1; i <= 100; i++) {
progressDialog.setProgress(i);
//100ms delay
}
Thanks
You can use Async Task for the purpose
in preExecute() method initialize loop index to 0;
in background process sleep thread for 10 seconds, and then call sendUpdate method to send progress
in postExecute update progress bar to progress get in parameter.
The following code may be helpful for you.
public void startProgress(View view) {
// Do something long
Runnable runnable = new Runnable() {
#Override
public void run() {
for (int i = 0; i <= 10; i++) {
final int value = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
#Override
public void run() {
progressDialog.setProgress(value);
}
});
}
}
};
new Thread(runnable).start();
}
A shorter solution without explicitly creating a new Thread would look like this:
final Handler handler = new Handler(Looper.getMainLooper());
final int sleepMs = 100;
for (int i = 1; i <= 100; ++i) {
handler.postDelayed(() -> progressDialog.setProgress(value), i * sleepMs);
}