In a chatting window I have a feature like self destructing message after a duration. For which I have a class which shows burning animation after the duration. While sending text the handler and timer I used in the below class is working fine for 2-3 times then some animation discrepancy is found like the fire isn't starting in exact location.
Apart from that while sending a file from file chooser fragment when it returns to the recyclerview of the chatwindow nothing happens until I touch the screen and scroll it a bit. While debugging I noticed that the first time when bindview is called after sending a file the handler and timer is not executing properly. Instead if I navigate back and enter the chatwindow again it's working perfectly. It burns the filemessage perfectly then.
The burner class:
public class Burner {
private static final TaggedLogger logger = new TaggedLogger("Burner");
public static void burnMessage(final TextMessageViewHolder holder, final BurnerListener listener) {
final int DURATION = 3000;
if (!holder.isBurning) {
final Handler handler = new Handler(Looper.getMainLooper());
final Animator.AnimatorListener animatorListener = new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) {
logger.log("onAnimationStart");
}
#Override
public void onAnimationEnd(Animator animator) {
listener.onBurnFinished();
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
};
holder.isBurning = true;
holder.fireAnimationHolder.setVisibility(View.VISIBLE);
holder.fireAnimationHolder.measure(0, 0);
Activity activity = (Activity) ChatProperties.getChatWindowContext();
if (activity != null) {
final ParticleSystem particleSystem = new ParticleSystem(activity, 48, R.drawable.fire_2, DURATION)
.setSpeedByComponentsRange(-0.025f, 0.0f, -0.06f, -0.08f)
.setAcceleration(0.00003f, 0)
.setInitialRotationRange(30, 45)
.addModifier(new AlphaModifier(255, 0, 200, 1000))
.addModifier(new ScaleModifier(0.5f, 2f, 0, 1000));
holder.fireAnimationHolder.post(new Runnable() {
#Override
public void run() {
holder.fireAnimationHolder.measure(0,0);
int fireAnimationHolderWidth = holder.fireAnimationHolder.getWidth();
// logger.log("fireAnimationHolderWidth = " + fireAnimationHolderWidth);
particleSystem.emit(holder.fireSource, 12);
holder.fireSource.animate().translationXBy(fireAnimationHolderWidth).setStartDelay(500).setDuration(DURATION).setListener(animatorListener).start();
holder.fireSourceController.setPivotX(0);
holder.fireSourceController.animate().alpha(1f).scaleX(1f).setDuration(DURATION).start();
}
});
new Timer().scheduleAtFixedRate(new TimerTask() {
int elapsedTime = 0;
#Override
public void run() {
elapsedTime += 300;
if (elapsedTime >= DURATION) {
handler.post(new Runnable() {
#Override
public void run() {
particleSystem.cancel();
}
});
this.cancel();
} else {
handler.post(new Runnable() {
#Override
public void run() {
particleSystem.updateEmitPoint(holder.fireSource, Gravity.CENTER);
}
});
}
}
}, 100, 300);
}
}
}
This burnMessage method is called from the bindview of the TextMessageViewholder's bindview method and FilemessageViewHolder extends TextMessageViewHoler class.
In TextMessageViewHolder's bindview method this function is called if it's a self destructing message:
private void handleMessageBurning(final Message message) {
if (message.imBurnTime > 0 && BurnMessageHelper.animatedBurnMessageEntryMap.containsKey(message.callerId)) {
Log.e("BurnThread","message to be burned");
Burner.burnMessage(this, new BurnerListener() {
#Override
public void onBurnFinished() {
if (ChatProperties.isGroupChat) {
DataHelper.getInstance().deleteGroupMessage(Target.target, message.callerId);
} else {
DataHelper.getInstance().deleteMessage(Target.target, message.callerId);
}
BurnMessageHelper.animatedBurnMessageEntryMap.remove(message.callerId);
isBurning = false;
}
});
} else {
fireAnimationHolder.setVisibility(View.GONE);
}
}
What is happening here? Does the thread of the timer can't run in main thread here due to some reason? Please help. Thanks.
N.B. I tried this in Marshmallow, Nougat and Oreo.
Related
I am now implementing the view pager and i want to continuously change the image one by one automatically for a particular periods of time may be once in 5 ms .
I also have to manually allow the user to swipe the images (it is working properly)
But the image is not automatically changing and when it reaches the last image it should come to the first image automatically
final Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
if (currentPage == strImages.length-1) {
currentPage = 0;
}
intro_images.setCurrentItem(currentPage++, true);
}
};
timer = new Timer(); // This will create a new Thread
timer .schedule(new TimerTask() { // task to be scheduled
#Override
public void run() {
handler.post(Update);
}
}, 500, 3000);
I used above code but it does not work properly
try this add addOnPageChangeListener to your viewPager like this
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position == strImages.length-1) {
currentPage = 0;
}
intro_images.setCurrentItem(currentPage++, true);
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
or for auto change page try this create a time task like this
Timer timer;
timer = new Timer();
timer.scheduleAtFixedRate(new RemindTask(), 0, 3000); // delay*/
private class RemindTask extends TimerTask {
int current = viewPager.getCurrentItem();
#Override
public void run() {
runOnUiThread(new Runnable() {
public void run() {
if (current < strImages.size()) {
viewPager.setCurrentItem(current);
current += 1;
} else {
current = 0;
viewPager.setCurrentItem(current);
}
}
});
}
}
The below code is to get the last position and to scroll to the 1st position:
pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener()
{
#Override
public void onPageScrolled(int position, float positionOffset,int positionOffsetPixels) {
if (position == Yourlist.size()-1) {
pager.setCurrentItem(0);
}
pager.setCurrentItem(position++,true);
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
The below code is For automatic change picture in every 10s using CountDown Timer:
- First you have to declare one array,which will contain your
images,then after you have to use below code.
int i=0;
new CountDownTimer(1000,1000)
{
#Override
public void onTick(long millisUntilFinished) {}
#Override
public void onFinish() {
iV.setImageDrawable(sdk.getContext().getResources().getDrawable(array[i]));
i++;
if(i== array.length-1) {i=0;}
start();
}
}.start();
You don't need the combination of Handler and TimerTask for this, You can use simple Handler like this using Handler.postDelayed
private Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
int totalCount = intro_images.getAdapter().getCount();
int currentItem = intro_images.getCurrentItem();
if (currentItem == (totalCount - 1)) {
currentItem = 0;
} else {
currentItem = currentItem + 1;
}
intro_images.setCurrentItem(currentItem, true);
handler.postDelayed(this, 3000);
}
};
#Override
protected void onResume() {
super.onResume();
handler.postDelayed(Update, 500);
}
#Override
protected void onPause() {
super.onPause();
if(handler != null) {
handler.removeCallbacks(Update);
}
}
just add listener to your viewpager to get the last position and to scroll to the 1st position.
vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
if(position==yourlist.length){
vp.setCurrentItem(0);
}
}
});
AnimationDrawable animation = new AnimationDrawable();
I have a recycler view. On a button click I want to remove all the items from the recyclerview but the items must be removed with animation.
I am able to remove all the items at once but I don't know how to remove them with animation. Thanks
It's old, but wish this helps someone else as it's already not answered yet; I have done it by deleting a single item at a time by simulating a swipe animation on this item, and post a delay before deleting the next item, and so on to the way down to the last item of the RecyclerView
Step No.1:
In your activity that holds the clear all button and the RecyclerView instance: Create a method of single item delete
private void deleteItem(View rowView, final int position) {
Animation anim = AnimationUtils.loadAnimation(requireContext(),
android.R.anim.slide_out_right);
anim.setDuration(300);
rowView.startAnimation(anim);
new Handler().postDelayed(new Runnable() {
public void run() {
if (myDataSource.size() == 0) {
addEmptyView(); // adding empty view instead of the RecyclerView
return;
}
myDataSource.remove(position); //Remove the current content from the array
myRVAdapter.notifyDataSetChanged(); //Refresh list
}
}, anim.getDuration());
}
Step No.2:
Create the method that will delete all RecyclerView list items >> call it in your button click callback.
boolean mStopHandler = false;
private void deleteAllItems() {
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
if (myDataSource.size() == 0) {
mStopHandler = true;
}
if (!mStopHandler) {
View v = myRecyclerView.findViewHolderForAdapterPosition(0).itemView;
deleteItem(v, 0);
} else {
handler.removeCallbacksAndMessages(null);
}
handler.postDelayed(this, 250);
}
};
requireActivity().runOnUiThread(runnable);
}
Also it's important to handle configuration change in manifest, activity section, as if the configuration changes while clearing your recycler view list, an exception will be raised
<activity
android:name=".activities.MainActivity"
android:configChanges="orientation|screenSize|keyboard"
android:label="#string/app_name">
...
</activity>
This is a pretty good library and what's better is the documentation for it. You can even insert durations for transitions and animations.
Also, remember that if you are using default animation, after calling myDataSet.remove(pos) using adapter.notifyDataSetChanged() while there is an animation ongoing will cause the animation to stop.
Extend BaseItemAnimator class of recyclerview-animators library:
MyAdapter adapter = new MyAdapter(null);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(new MyScaleInLeftAnimator());
findViewById(R.id.button).setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View view) {
int count = adapter.getItemCount();
adapter.clear();
adapter.notifyItemRangeRemoved(0, count);
}
}
);
...
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder{
private ArrayList<String> mItems;
...
public void clear() {
if (mItems != null) {
mItems.clear();
}
}
}
...
public class MyScaleInLeftAnimator extends BaseItemAnimator {
private long lastRemoval;
private int removeCount;
public MyScaleInLeftAnimator() {
lastRemoval = 0;
removeCount = 0;
}
public MyScaleInLeftAnimator(Interpolator interpolator) {
mInterpolator = interpolator;
lastRemoval = 0;
removeCount = 0;
}
#Override protected void preAnimateRemoveImpl(RecyclerView.ViewHolder holder) {
ViewCompat.setPivotX(holder.itemView, 0);
}
#Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
long time = System.currentTimeMillis();
long d = time - lastRemoval;
if (d < 100) {
removeCount++;
} else {
removeCount = 0;
}
lastRemoval = time;
ViewCompat.animate(holder.itemView)
.scaleX(0)
.scaleY(0)
.setDuration(getRemoveDuration())
.setInterpolator(mInterpolator)
.setListener(new DefaultRemoveVpaListener(holder))
.setStartDelay(removeCount * 100)
.start();
}
#Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) {
ViewCompat.setPivotX(holder.itemView, 0);
ViewCompat.setScaleX(holder.itemView, 0);
ViewCompat.setScaleY(holder.itemView, 0);
}
#Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) {
ViewCompat.animate(holder.itemView)
.scaleX(1)
.scaleY(1)
.setDuration(getAddDuration())
.setInterpolator(mInterpolator)
.setListener(new DefaultAddVpaListener(holder))
.setStartDelay(getAddDelay(holder))
.start();
}
}
This is how I have done without using any libraries - by inserting delays in the loop to remove items & restore (if needed)
clearItemsView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final List<LineItem> lineItemsCopy = new ArrayList<>(lineItems);
new Thread(new Runnable() {
#Override
public void run() {
for (int i=0; i<lineItemsCopy.size(); i++) {
runOnUiThread(new Runnable() {
#Override
public void run() {
salesOrderItemListAdapter.removeItem(0);
}
});
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
Snackbar snackbar = Snackbar.make(coordinatorLayout, getString(R.string.items_cleared_message), Snackbar.LENGTH_LONG)
.setAction(getString(R.string.label_undo), new View.OnClickListener() {
#Override
public void onClick(View v) {
new Thread(new Runnable() {
#Override
public void run() {
for (int i=0; i<lineItemsCopy.size(); i++) {
final int finalI = i;
runOnUiThread(new Runnable() {
#Override
public void run() {
salesOrderItemListAdapter.restoreItem(lineItemsCopy.get(finalI), 0);
}
});
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}).setActionTextColor(Color.YELLOW);
snackbar.show();
}
});
I want to remove items from a gridView one by one. the problem is that are all removed once !
This is my code
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
boolean sw = false;
while(!sw)
{
int i=0;
if(!adapter.isEmpty())
{
adapter.removeItem(i);
i+=1;
} else sw = true;
}
}
}, 700);
in adapter I've created a remove function.. This it is
public void removeItem(final int position)
{
data.remove(position);
notifyDataSetChanged();
}
Advices ?
The problem is you are running all the remove in one go, try with this
public void removeAllItem()
{
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run()
{
if(!adapter.isEmpty())
{
adapter.removeLastItem();
removeAllItem();
}
}
}, 700);
}
and removeLastItem() is
public void removeLastItem()
{
int lastIndex = data.size() - 1;
data.remove(lastIndex);
notifyDataSetChanged();
}
Which will remove every item from the last index every 700 miliseconds.
Declare object in class level
boolean sw = false;
Change code like below
while (!sw) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
int i = 0;
if (!adapter.isEmpty()) {
adapter.removeItem(i);
i += 1;
} else sw = true;
}
}, 700);
}
Hope it will help you !!
Using Android studio, I am trying to make an app that gets data from a web-service and display the data and updates the view every 5 sec or when the data on the web-service changes. With this I am trying to change the colours on some button based on an int, the int changes and the color on the button changes when I apply buttons(); to another button and then presses it but I want it to update by itself.
When I used a while loop the app gets stuck
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
buttons();
}
});
The runOnUiThread is placed in the onCreate.
using run on UI thread will cause your UI to freeze , try using a timer task instead .
example :
#Override
public void StopTimerTask() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
public void StartTimer() {
timer = new Timer();
initializeTimerTask();
int UpdateTime = Integer.valueOf(UserSettings.getString("Update", "60000"));
timer.schedule(doAsynchronousTask, 0, YOURTIME);
}
public void initializeTimerTask() {
doAsynchronousTask = new TimerTask() {
#Override
public void run() {
myHandler.post(new Runnable() {
public void run() {
YOUR LOGIC HERE
}
});
}
};
}
doing this where u just put another class into main activity was succesful only problem is that it have to be in my main class
public class updategui extends TimerTask {
Activity context;
Timer timer;
public updategui(Activity context, int seconds) {
this.context = context;
timer = new Timer();
timer.schedule(this,
seconds * 1000, // initial delay
seconds * 1000); // subsequent rate
}
#Override
public void run() {
if(context == null || context.isFinishing()) {
this.cancel();
return;
}
context.runOnUiThread(new Runnable() {
#Override
public void run() {
try {
buttons();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}}
I'm new to android and i have a problem
I used same code to repeat and delay method for two class. One class work fine but other not. I don't know why. This is my code
SpeedMeterFragment.java
public class SpeedMeterFragment extends Fragment {
....
public void speedMeterBefore() {
totalRxBytesBefore = TrafficStats.getTotalRxBytes();
Log.d("test", "Before: " + String.valueOf(totalRxBytesBefore));
}
public void speedMeterAfter() {
totalRxBytesAfter = TrafficStats.getTotalRxBytes();
Log.d("test", "After: " + String.valueOf(totalRxBytesAfter));
}
public void speedMeterDifference() {
totalRxBytesDifference = totalRxBytesAfter - totalRxBytesBefore;
tvTest.setText(String.valueOf(totalRxBytesDifference/1024) + " kb/s");
Log.d("test", "Difference: " + String.valueOf(totalRxBytesDifference));
}
public void speedMeter() {
handler = new Handler();
handler.post(runnable = new Runnable() {
#Override
public void run() {
speedMeterBefore();
final Handler handler1 = new Handler();
handler1.postDelayed(new Runnable() {
#Override
public void run() {
speedMeterAfter();
speedMeterDifference();
}
}, 1000);
handler.postDelayed(this, 1000);
}
});
}
}
and SaveDataUseage.java
public class SaveDataUseage extends BroadcastReceiver {
...
public void onReceive(Context context, Intent intent) {
...
savePreference();
}
public void savePreference() {
...
measureDataMB();
}
public void measureDataMBBefore() {
dataMBBefore = TrafficStats.getTotalRxBytes() / 1048576;
}
public void measureDataMBAfter() {
dataMBAfter = TrafficStats.getTotalRxBytes() / 1048576;
}
public void measureDataMBDifference() {
dataMBDifference = dataMBAfter - dataMBBefore;
}
public void measureDataMB() {
handler = new Handler();
handler.post(runnable = new Runnable() {
#Override
public void run() {
measureDataMBBefore();
final Handler handler1 = new Handler();
handler1.postDelayed(new Runnable() {
#Override
public void run() {
measureDataMBAfter();
measureDataMBDifference();
}
}, 1000);
handler.postDelayed(this, 1000);
}
});
}
}
SpeedMeterFragment work fine, but not SaveDataUseage
Anybody help me?
Sorry for my bad English :)
handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
//Your method called here
handler.postDelayed(this, 1000);
}
};
handler.postDelayed(r, 1000);
this code will repeat method after every second
handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
//Your method called here
MyMethod(isCalledFromHandeler);
handler.postDelayed(this, 1000);
}
};
handler.postDelayed(r, 1000);
use boolean argument isCalledFromHandeler to recognition the caller of method.
and then use
if(isCalledFromHandeler){
// Do Not call the Child Method
} else{
// Call the Child Method
}
Although its eight month old question, I also faced the similar problem and in solution I extended Handler class to AdvanceHandler with the postRepeated method.
Sample Usage
AdvanceHandler handler = new AdvanceHandler();
handler.postRepeated(new Runnable() {
#Override
public void run() {
//Your repeated task
}
}, 5000) //After every 5 seconds