This question already has answers here:
Android SeekBar set progress value
(10 answers)
Closed 4 years ago.
I am using a Seekbar in a fragment and need to move the Seekbar to different positions. However, setProgress(xxx) is not working.
How do you trigger this method programmatically for a Seekbar: public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)? Moving the Seekbar manually works fine.
It's a Bug in ProgressBar!
The setProgress(...) seems to not trigger the update on the drawable if the same value is passed again. But it's not triggered during the setMax, too. So the update is missing.
To solve this, I'm just doing a bar.setProgress(0) before each update... this is only a workaround, but it works for me as expected:
bar.setProgress(0); // call these two methods before setting progress.
bar.setMax(20);
bar.setProgress(20);
Second Option.
mSeekBar.post(new Runnable() {
#Override
public void run() {
mSeekBar.setProgress(percentOfFullVolume);
}
});
it may also work for someone.
Try to set max value first and then set the progress.
I add this answer as this can help someone in the future.
In my case, I also thought setProgress was not working properly but here is my mistake.
increaseIncomeSeekBar.setProgress(progress);
increaseIncomeSeekBar.setMax(maxProgress);
For info progress and maxProgress equals 2000 and 12000. But when displaying the SeekBar, it seems like the progress was at 0 and thus not working properly.
You have to be aware of this which is pretty logical when you know it:
first SeekBar max = 100.
when you set a progress > max then progress = max.
so you need to set a max value before setting a progress value.
I add a comment to this topic because it happenned to me and it took me hours to figure out what the problem was. The bug is still active and the setProgress() method does not work properly sometimes.
I have a MainActivity which commit and replace Fragments and transmit the SeekBar value inside the bundle of each Fragments. The User changes the value of the Seekbar and its progress are put in the Bundle. Then when the User switch of Fragment, the new one get the SeekBar progress.
It works perfectly fine to transmit it this way and I always have the right value on my progress variable in the second Fragment. The problem appears when I use setProgress(theProgressValueTransmitted) method. This set my SeekBar progress only the first time I replace the first Fragment. After that, it never changes it wheareas the value of progress is still the right one.
I used :
int seekBarProgress;
Bundle bundle = this.getArguments();
if (bundle != null) {
seekBarProgress = bundle.getInt("seekBarProgress");
mySeekBar.post(new Runnable() {
#Override
public void run() {
eventListTimeSeekBar.setProgress(seekBarProgress);
}
});
}
And this is the only way I can make this work. Hope it could help someone with the same problem. This post is more than 4 years old, I don't even understand how this bug can still exist since it has been reported.
SeekBar.setProgress() should work just fine. Are you sure your code is executing on the UI thread? If not, then this would be the obvious explanation. Check the example in the API doc for ProgressBar
https://developer.android.com/reference/android/widget/ProgressBar.html
There they show how to bring the execution back to the main thread.
Nowadays everything seems to work as expected.
Post is not needed anymore.
Just do:
seekBar.setMax(200);
seekBar.setProgress(50);
(order doesn't matter)
Most of the time, SeekBar works fine. Sometimes, it won't.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//...
seekBar.setProgress(value);
//...
}
After I refresh the fragment view by detach() + attach(), the code does not work. My solution is to apply setProgress() within onViewStateRestored().
#Override
public void onViewStateRestored(Bundle inState) {
//...
seekBar.setProgress(value);
//...
}
Related
I have a progress dialog that takes 3 minutes to fill the progress.
If i get response from server earlier I want to fill progress to 100%, and then disables it.
This is my code, but it does not fill the progress bar to 100%.
progressBar.setProgress(100);
progressBar.cancel();
progressBarLayout.setVisibility(View.GONE)
I have a separate layout for progress dialog, that i disable when i set progressBar to 100. May be that's the reason it fails to render the progress dialog.
There might be an issue here:
The progressBar has a setMax method. So if your max is set to 2 (for example) if you use progressBar.setProgress(1); it will advance to 50%.
If you want to always fill it completely, perhaps using something like:
progressBar.setProgress(progressBar.getMax());
Possible issue:
Im not sure if you should change this in the UIThread, because you are touching the UI, I would recommend to check it.
Hope this helps.
Your code fills it to 100% but immediately makes it disappear, hence you're not seeing it. Try adding the code about turning your bar invisible in a runnable that starts only after you get the confirmation from the server
Runnable r = new Runnable() {
#Override
public void run() {
progressBarLayout.setVisibility(View.GONE)
}
}
then create a handler for ypur runnable:
Handler handler = new Handler();
When you get the response from the server tell the handler to wait for x seconds and run the runnable
handler.postDelayed(r, 2000); //here it waits for 2 seconds (2000 mills)
I'm using android.support.v4.widget.SwipeRefreshLayout with android.support.v7.widget.RecyclerView.
On fragment view creation I need to show SwipeRefreshLayout animation. But when I call setRefreshing(true) nothing happens. But when I refresh data animation changes.
I suppose that the animation isn't showing without child inside SwipeRefreshLayout.
How to show this animation in the best way?
This is a reported bug (reported here) and a workaround is available:
mSwipeRefreshLayout.setProgressViewOffset(false, 0,
(int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
24,
getResources().getDisplayMetrics()));
mSwipeRefreshLayout.setRefreshing(true);
It depends on which API level you're building under - if you're using up to API 20 then you can just turn on setRefreshing(true), this will run the animation in the ActionBar, but in API 21 (Material Design) they changed the progress to be a spinner than is "pulled into view" before it spins
You have 2 ways of getting around this in API 21:
1) shift the spinner down with setProgressViewOffset(), but remember to shift it back up afterwords (note that this works in px, while setDistanceToTriggerSync() uses dp)
2) make a duplicate spinner that is displayed when you're loading the data
The more code-efficient solution is to use the existing spinner, but you have to be careful that you do reset its position
If you need to calculate the pixel density, you can grab it from:
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
float scale = metrics.density;
Thanks, guys.
I`ve just realised that the easiest solution is to call method measure(int, int) with non-zero values) before calling setRefreshing(true). The problem is happening because setRefreshing(true) is calling before onMeasure.
So you need to call:
swipeRefreshLayout.measure(initialWidth, initialHeight);
swipeRefreshLayout.setRefreshing(true);
SwipeRefreshLayout must contain a child to work properly. Make sure you initialize it before using it anywhere!
you must call setRefreshing(true) after onDraw(), else it would not display animation.
so, in Activity, you can do it in onCreate(), like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// other codes
new Handler().post(new Runnable() {
#Override
public void run() {
swipeRefreshLayout.setRefreshing(true);
refreshing = true;
refreshData();
}
}
}
in fragment, you can put these codes in onActivityCreated().
I have a view that contains a user's drawing. When clicking a 'play' button I want to have a loop that draws the image from start to finish.
In attempting to do this I basically have a loop inside of an onClickListener for the play button that essentially invalidates the view, waits, sets the next set of points and invalidates again.
The problem is that the image doesn't actually redraw until after the entire image is draw. So essentially nothing happens and then it's all of a sudden done. After doing some research, my assumption is that invalidate is telling the main UI thread to redraw when it gets time, but it's already hung up on dealing with the listener, so it doesn't actually draw until the loop is finished. I've tried calling onDraw directly, using postInvalidate, and just about any other ridiculous thing I can think of, but I'm not sure how to get this to work.
Here are some code snippets:
In my PaintActivity(Which has a PaintAreaView)
The scrubber indicates the progress of the drawing. The first to lines in onProgressChanged works when I manually use the seek bar, but not when pressing play, which is why I tried adding all the extras.
_scrubber.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
_paintAreaView.setPointsToDraw(_scrubber.getProgress());
_paintAreaView.invalidate();
_mainViewGroup.invalidate();
_paintAreaView.postInvalidate();
_mainViewGroup.postInvalidate();
_paintAreaView.drawPoints(_paintAreaView.getCanvas());
_paintAreaView.invalidate();
}...
My Play Button stuff:
play.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
...
playDrawing();
...
}
});
private void playDrawing()
{
int endPoint = getPaintPointCount();
while(_scrubber.getProgress() < endPoint)
{
_scrubber.incrementProgressBy(1);
try{
Thread.sleep(10);
}catch(InterruptedException e)
{
Thread.currentThread().interrupt();
}
//_paintAreaView.setPointsToDraw(_scrubber.getProgress());
//_paintAreaView.invalidate();
//_mainViewGroup.invalidate();
if(_isPaused)
break;
}
}
I'm not a huge fan of using that sleep, but I've struggled with other solutions and that's my next thing to research. But for this question, incrementing the progress bar does successfully call onProgressChanged, but again it just isn't drawing the view until it's out of that play button click listener.
If I have to wait until I'm out of the listener to get this to work, I'm new enough that I'm not sure how I would approach that.
Sorry for my bad code, any help is much appreciated to help me understand what's happening here and what I need to understand to get this to work.
Thank you!
I got it to work, I'm not sure if all I did was necessary, but here are the changes I made based on my Main UI thread theory being the problem.
I created a class Thread member variable:
private Thread _drawThread;
In onCreate():
_drawThread = new Thread(){
public void run(){
playDrawing();
}
};
play Button onClick:
play.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
...
_drawThread.start();
}
});
Seems to be doing just what I want! Except the app crashes the second time I hit play :) From here I don't think it'll be to bad to figure out that issue though.
EDIT-----
Creating the Thread in my play button onClickListener instead of having it be a member variable fixed the crashing. I incorrectly assumed the thread would be destroyed as soon as the listener went out of scope on the main thread, but I guess that's not the case.
I'm using robotium 3.1 and I'd like to wait for a view to disappear, is there some way I can do that easily? My current way involves a ugly busy-loop with sleeps that makes no one happy.
To clarify what I'd like to happen:
waitForView(<View>) //The view appears
//The view is visible for a few seconds
waitForViewNotThere(<View>) //waits until the view has disappeared
The view that appears doesn't contain any text or such either. Any input is very much appreciated.
This is how:
final TextView helloWorldText = solo.getText("Hello world!");
solo.waitForCondition(new Condition() {
#Override
public boolean isSatisfied() {
return helloWorldText.getVisibility() == View.INVISIBLE;
}
}, 10000);
Whatever you do you are probably going to have some sort of sleep in the loop. (If you look at robotiums source it also uses sleeps). You can keep them to a minimum by using the waitforidlesync method on instrumentation that waits for the Ui thread to become idle.
if you want to wait for a view to disappear, use solo.waitForDialogToClose(long timeout).
Parameters :
timeout - the amount of time in milliseconds to wait.
returns : true if the Dialog is closed before the timeout and false if it is not closed.
I'm currently fighting against the OnLongClickListener on Android Api Lvl 8.
Take this code:
this.webView.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
System.out.println("long click");
return true;
}
});
It works perfectly. I can press anywhere on the WebView and the event triggers every time.
Now take a look at this one:
this.webView.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
final EditText editText = getUrlTextField();
switch (editText.getVisibility()) {
case View.VISIBLE:
editText.setVisibility(View.GONE);
return true;
case View.GONE:
editText.setVisibility(View.VISIBLE);
return true;
default:
return false;
}
}
});
Assuming the URL EditText components is currently visible, it gets gone from the display and should be shown again when another long click event is triggered.
But if you run this, the event just works once (!) when one performs a long click on any position on the WebView. To make things complicated, the long click works again when it is performed on a link on the website...
Can anyone explain if it is a bug in the sdk and/or if there is a mistake in my thinking how the OnLongClickListener is working?!? :/
EDIT:
I've run now several different scenarios on a Nexus One and come to following conclussion: Changing the layout on runtime more or less kills the OnLongClickListener... I haven't found a way to get it work reliably at all...
I would really appreciate if anyone could give me a hint... I'm at my wits end :(
Personnally, I ended up by re-setting the listener after each relayout.
I've run into this issue as well. It seems that if the view layout changes in a way that child view bounds need to be modified (i.e. TextView is wrap_content width and you set its text to something longer/shorter than it was before), views in the hierarchy will have their onStartTemporaryDetach method called (most likely due to a layout pass, although I haven't dug deep enough to find out for sure). If you look at the source for View that onStartTemporaryDetach ultimately unsets the pressed state of the view.
Changing the views in your layout that will be updated periodically to have bounds that will not change regardless of the value you set, will fix the issue. Although, that is still not awesome.