On target reached in Smooth scroller in android - java

Here is how my app looks
I have a horizontal suppressed recyclerview and once the user clicks an answer it smoothscrolls to the next one
, tho I wanted to prevent clicks while the smooth scrolling is still going
I used a handler and had many try and error until I got it to work on phones,
however, it isn't practical and doesn't work the same for every phone
public void setSmoothScroller(final float speed){
smoothScroller = new LinearSmoothScroller(getApplicationContext()) {
private final float SPEED = speed;
#Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return SPEED / displayMetrics.densityDpi;
}
};
}
it could be complicated trying to explain but all i need is instead of using handlers to enable touching again , is there a way to make it automatically perform a function on reaching the specific position
#Override
public void onAnswered(final int position) {
if (answered < bank){
handler.postDelayed(new Runnable() {
#Override
public void run() {
scrollingState = true;
recyclerView.suppressLayout(true);
setSmoothScroller(300f);
smoothScroller.setTargetPosition(position + 1);
layoutManager.startSmoothScroll(smoothScroller);
}
}, 1000);
handler.postDelayed(new Runnable() {
#Override
public void run() {
enableTouch();
}
}, 3150);
}
}

Related

How do I stop all handlers running in the background?

I'm writing an application that is creating audio players dynamically. By pressing a button, I want all handlers to stop and start again when I call them. The handlers, basically, are responsible for updating the seek-bar of the audio player. the seek-bar and the play button are being creating into a linearlayout, and this linear layout is being adding into a parent linearlayout. this way, for example, if I decide by code to create 4 audio players and I want them to be able to play the audio together, I put in a loop the creation of a 4 linearlayouts and add them to the parent linearlayout. the code more or less looks like this (this is just the relevant part, not the real code...):
for (int i = 0; i < 4; i++)
{
LinearLayout audioLayout = new LinearLayout(this);
audioLayout.setOrientation(LinearLayout.HORIZONTAL);
audioLayout.setPadding(20, 20, 20, 20);
final Button btnAudioPlay = new Button(this);
btnAudioPlay.setBackgroundResource(R.drawable.play);
LinearLayout.LayoutParams playButtonParams = new LinearLayout.LayoutParams(50, 50);
playButtonParams.setMargins(20, 0, 20, 0);
audioLayout.addView(btnAudioPlay, playButtonParams);
final SeekBar sbAudioPosition = new SeekBar(this);
LinearLayout.LayoutParams seekbarParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
seekbarParams.setMargins(20, 0, 20, 0);
audioLayout.addView(sbAudioPosition, seekbarParams);
parentLayout.addView(audioLayout);
final MediaPlayer mpAudio = MediaPlayer.create(this, Uri.fromFile(audFile));
mpAudio.setLooping(false);
mpAudio.seekTo(0);
int totalTime = mpAudio.getDuration();
sbAudioPosition.setMax(totalTime);
sbAudioPosition.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
{
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
if (fromUser)
{
mpAudio.seekTo(progress);
sbAudioPosition.setProgress(progress);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
mpAudio.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
btnAudioPlay.setBackgroundResource(R.drawable.play);
sbAudioPosition.setProgress(0);
}
});
btnAudioPlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!mpAudio.isPlaying()) {
mpAudio.start();
btnAudioPlay.setBackgroundResource(R.drawable.pause);
final Handler handler = new Handler();
runOnUiThread(new Runnable() {
#Override
public void run() {
sbAudioPosition.setMax((int) mpAudio.getDuration());
int currentPossition = mpAudio.getCurrentPosition();
sbAudioPosition.setProgress(currentPossition);
handler.postDelayed(this, 0);
}
});
}
else {
mpAudio.pause();
btnAudioPlay.setBackgroundResource(R.drawable.play);
}
}
});
}
When I press a button, I'm updating the view, and I want all handlers to stop, but it doesn't happening... I tried to declare, before the onCreate the Handler handler = null; (instead of the final handler) and then in the linearlayout make it new and it worked, but in the method of the button I called handler.removeCallbacksAndMessages(null); and it didn't work. I tried to declare the runable as well and call handler.removeCallbacks(runOnUiThread);, but my application crashes... :(
What do I need to do to solve it?
If I want that one audio play will stop the current audio playing, is it possible? will it solve my problem?
Sorry for the length and thanks for the helpers. :)
You can use the Handler as a member of your Activity something like:
private Handler mHandler = new Handler();
private Runnable mRunnable = new Runnable() {
#Override
public void run() {
sbAudioPosition.setMax((int) mpAudio.getDuration());
int currentPossition = mpAudio.getCurrentPosition();
sbAudioPosition.setProgress(currentPossition);
mHandler.post(this);
}
};
Then use the mHandler when you make execute mHandler.postDelayed(runnable,delay). And then you can call mHandler.removeCallbacks(null); when you want to remove them all.
Note: You can remove specific runnables like this: mHandler.removeCallbacks(mRunnable);

How would I animate multiple views at the same time over different points?

I'm quite new to Android Studio, and I would like to animate multiple images at the same time, over multiple coordinates.
Let's say I would like to move image A from 0/0 to 100/100, and then to 200/200; at the same time (eg. by the click of a button), image B shall be moved from 0/100 to 100/100, and then to 200/100. All with simple translations.
(As a plus, I would like to be able to set the duration of the movements independently.)
When the animations have finished, there should be an event (somthing like OnAnimationFinish?) to trigger other things, like enabling the start button again.
What would be the most effective way to do this? I know there is an AnimationSet in Android to store multiple animations, but I don't know if this is helpful here.
Thanks in advance!
You can use ObjectAnimator with AnimatorSet to play multiple animations simultaneously with proper listeners.
For example():
val imageXAnimator = ObjectAnimator.ofFloat(imageView, "translationX", translateX)
val imageYAnimator = ObjectAnimator.ofFloat(imageView, "translationY", translateY)
val imageAlphaAnimator = ObjectAnimator.ofFloat(imageView, "alpha", if (reverse) 0F else 1F)
val animationSet = AnimatorSet()
animationSet.playTogether(
imageXAnimator,
imageYAnimator,
imageAlphaAnimator)
animationSet.interpolator = DecelerateInterpolator()
animationSet.duration = 1000
animationSet.addListener(
onStart = {
//When animation is started
},
onEnd = {
//When animation finishes
}
)
animationSet.start()
Or a simple extension in kotlin which can be called on a specific View:
inline fun View.animateTranslationY(translationY: Float, duration: Long = 1000, startDelay: Long = 0) {
val translationYAnimator = ObjectAnimator.ofFloat(this, "translationY", translationY)
translationYAnimator.duration = duration
translationYAnimator.startDelay = startDelay
translationYAnimator.interpolator = LinearInterpolator()
translationYAnimator.addAnimatorListener(
onStart = { },
onEnd = { }
)
translationYAnimator.start()
}
You can use the "View.animate()" method that allow you to access to its sub-methods and events to manage all these stuff. You just have to call "animate()" on a View (TextView, ImageView, EditBox, etc..) and then call its sub-methods to decide what kind of animation to perform: motion, rotation, changing transparency, etc... For each animation you can set a listener that will be called at Start and/or at the End of animation.
Consider these two methods for both views
private void animateFirstView(final ImageView first, long durationFirst, final long durationSecond) {
first.setTranslationX(0);
first.setTranslationY(0);
first.animate().translationX(100).translationY(100).setDuration(durationFirst).setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
first.animate().translationY(200).translationX(200).setDuration(durationSecond).setListener(null).setInterpolator(new LinearInterpolator()).start();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
}).setInterpolator(new LinearInterpolator()).start();
}
private void animateSecondView(final ImageView second, long durationFirst, final long durationSecond) {
second.setTranslationX(0);
second.setTranslationY(100);
second.animate().translationX(100).translationY(100).setDuration(durationFirst).setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
second.animate().translationY(200).translationX(100).setDuration(durationSecond).setListener(null).setInterpolator(new LinearInterpolator()).start();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
}).setInterpolator(new LinearInterpolator()).start();
}
You can do this:
private void playAnimation(final View view, final int endRangeY) {
view.animate().translationY(endRangeY) //endRangeY where you want to end the animation
.alpha(0.0f)
.setDuration(3000)
.setListener(new AnimatorListenerAdapter() {
#Override public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
}

java.lang.illegalStateException when getting mediaPlayer.getCurrentPosition and SeekBar going to Intial on Pause

I'm getting java.lang.IllegalStateException in my Android Music Player Application every time when I closes the Activity
mCurrentPosition = mediaPlayer.getCurrentPosition()/1000; // In milliseconds
I've tried nearly all the codes available and even all at java.lang.illegalStateException randomly occurs when getting mediaPlayer.getCurrentPosition also
Here's my Java code where I'm using it:
protected void initializeSeekBar(){
mSeekBar.setMax(mediaPlayer.getDuration()/1000);
mRunnable = new Runnable() {
#Override
public void run() {
int mCurrentPosition;
if(mediaPlayer!=null && mediaPlayer.isPlaying()){
mCurrentPosition = mediaPlayer.getCurrentPosition()/1000; // In milliseconds
}
else {
mCurrentPosition = 0;
}
if (mSeekBar != null) {
mSeekBar.setProgress(mCurrentPosition);
getAudioStats();
}
handler.postDelayed(mRunnable,1000);
}
};
handler.postDelayed(mRunnable,1000);
}
Please Help me out of this. Thanks in Advance
Edit 1
Also, my Seekbar goes to start when the music pauses and on play, it continues from where it was before pausing
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
getAudioStats();
initializeSeekBar();
if(mediaPlayer != null && fromUser) {
mediaPlayer.seekTo(progress * 1000);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
protected void getAudioStats(){
long duration = mediaPlayer.getDuration()/1000; // In milliseconds
long due = (mediaPlayer.getDuration() - mediaPlayer.getCurrentPosition())/1000;
long pass = duration - due;
String test = DateUtils.formatElapsedTime(pass);
String test1 = DateUtils.formatElapsedTime(due);
current_time.setText("" + test);
//mDuration.setText("" + duration + " seconds");
total_time.setText("" + test1);
}
You need to remove your runnable from the handler queue when the activity closes (or, better yet, when it pauses). Try putting this in your activity's onPause() method:
handler.removeCallbacks(mRunnable);
For additional robustness, inside the runnable itself, you should only re-queue the runnable if the activity is still active. So instead of this:
handler.postDelayed(mRunnable,1000);
you should do this (assuming this code is in the activity class):
if (!isDestroyed()) {
handler.postDelayed(mRunnable,1000);
}
Also make sure you are stopping the media player before the activity is destroyed.
P.S. If you call initializeSeekBar() multiple times, you will be reassigning mRunnable and leaving the old one in the handler queue where it can cause trouble later. To fix this, you should add this at the start of initializeSeekBar():
protected void initializeSeekBar(){
if (mRunnable != null) {
handler.removeCallbacks(mRunnable);
}
// ... the rest of the method that you currently have

CountDownTimer variable

I have a major problem about changing the value from a constructor
This is an app for flashlights. It will connect to a site to check the interval value (ex. 500 ms) and it will store it to an variable named frum_timer every 2 seconds.
Here's a object for that.
(also it will update a boolean if there's a new value)
FlashActivity = new CountDownTimer(40000, frum_timer) {
#Override
public void onTick(long millisUntilFinished) {
stats_of_run = true;
if (bool) {
final_form.setText("\\Flash ON//");
// CAMERA INSTRUCTIONS FOR OPENING FLASH
bool = false;
frumtimer_stats.setText("Speed:" + frum_timer);
} else {
stats_of_run = true;
final_form.setText("->Flash OFF-<");
// CAMERA INSTRUCTIONS FOR CLOSING FLASH
bool = true;
}
}
#Override
public void onFinish() {
}
}.start();
I made another CountDownTimer object with a refresh rate of 200 seconds to make sure the frum_timer from FlashActivity object will take change.
and using this
frumtimer_stats.setText("Speed:" + frum_timer);
in FlashActivity for displaying the variable.
However, after the variable frum_timer is changed, the FlashLight keep going again and again at the same old speed even if a made an FlashActivity.cancel() followed by FlashActivity.start()
Can someone give me some help?
Full link of code:
https://mycodestock.com/public/snippet/14395
Summary:
1.You start the app.
2. Refresh countdown starts in 2 sec
3. When there is a frum_timer the FlashActivity will start. After 2 sec, if there is another value stored on frum_timer, the actual FlashActivity will be canceled and will start a new one.
4.The problem is the new FlashActivity start but with the old frum_timer
I used:
public void startProgress(View view) {
// do something long
Runnable runnable = new Runnable() {
#Override
public void run() {
for (int i = 0; i <= 20; i++) {
final int value = i;
doFakeWork();
progress.post(new Runnable() {
#Override
public void run() {
// text.setText("Updating");
progress.setMax(20);
progress.setProgress(value);
}
});
}
}
};
new Thread(runnable).start();
}
private void doFakeWork() {
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

Avoid multi-click in image view android

I try to use this code to prevent multi-click in ImageView but it doesn't help.
Boolean isClicked = false;
#Override
public void onClick(View v)
{
if (v == imgClick && !isClicked)
{
//lock the image
isClicked = true;
Log.d(TAG, "button click");
try
{
//I try to do some thing and then release the image view
Thread.sleep(2000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
isClicked = false;
}
}
In the log cat, I can see 5 lines "button click" when I click on ImageView for 5 times as quickly as possible. I can see the log cat print the first line, wait for a while (2 seconds) and then print the next line. I think when I click the ImageView, the fired event is moved to queue in order, isn't it?. So how can I stop that?
I also try to use setEnable() or setClickable() instead of isClicked variable but it doesn't work too.
Just try this working code
Boolean canClick = true; //make global variable
Handler myHandler = new Handler();
#Override
public void onClick(View v)
{
if (canClick)
{
canClick= false; //lock the image
myHandler.postDelayed(mMyRunnable, 2000);
//perform your action here
}
}
/* give some delay..*/
private Runnable mMyRunnable = new Runnable()
{
#Override
public void run()
{
canClick = true;
myHandler.removeMessages(0);
}
};
Instead of sleeping in 2 seconds, I use some task like doSomeThing() method (has accessed UI thread), and I don't know when it completed. So how can I try your way?
//I referred this android link. You can handle thread more efficiently but i hope below code will work for you..
//you try this and
Boolean canClick = true; //make global variable
public void onClick(View v) {
if(canClick){
new DownloadImageTask().execute();
}
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... urls) {
Log.d("MSG","Clicked");
canClick =false;
//perform your long operation here
return null;
}
protected void onPostExecute(Bitmap result) {
canClick =true;
}
}
You could keep track of the last consumed click upon your View, and based on it either perform the necessary actions, or simply return:
private long calcTime;
private boolean isClickedLately(final long millisToWait)
{
if (System.currentTimeMillis() - calcTime < millisToWait)
return true;
return false;
}
#Override
public void onClick(View v)
{
if (isClickedLately(2000))
return;
calcTime = System.currentTimeMillis();
Log.d(TAG, "consuming button click");
// perform the necessary actions
}
With the millisToWait parameter you can adjust the threshold of "waiting", but if you know that you want to wait exactly 2 seconds between two consecutive clicks, you can eliminate it.
This way you don't have to deal with Threads, which is good, since it's not a great idea to make the gui thread wait.

Categories