Fixed SeekBar not animating after device rotation
This commit is contained in:
parent
b03c6123e9
commit
2daf03b754
@ -0,0 +1,29 @@
|
||||
package com.philliphsu.clock2;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v7.widget.AppCompatSeekBar;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
/**
|
||||
* A SeekBar that cannot be touch controlled.
|
||||
*/
|
||||
public class UntouchableSeekBar extends AppCompatSeekBar {
|
||||
|
||||
public UntouchableSeekBar(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public UntouchableSeekBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public UntouchableSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean onTouchEvent(MotionEvent event) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -21,7 +21,11 @@ import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
@ -64,6 +68,9 @@ public class ChronometerWithMillis extends TextView {
|
||||
private StringBuilder mFormatBuilder;
|
||||
private OnChronometerTickListener mOnChronometerTickListener;
|
||||
private StringBuilder mRecycle = new StringBuilder(8);
|
||||
private boolean mApplySizeSpan;
|
||||
|
||||
private static final RelativeSizeSpan SIZE_SPAN = new RelativeSizeSpan(0.5f);
|
||||
|
||||
private static final int TICK_WHAT = 2;
|
||||
|
||||
@ -223,6 +230,11 @@ public class ChronometerWithMillis extends TextView {
|
||||
return mRunning;
|
||||
}
|
||||
|
||||
public void setApplySizeSpan(boolean applySizeSpan) {
|
||||
mApplySizeSpan = applySizeSpan;
|
||||
init(); // update text again
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
@ -272,7 +284,14 @@ public class ChronometerWithMillis extends TextView {
|
||||
// It looks like Google's Clock app strictly uses .
|
||||
".%02d", // The . before % is not a format specifier
|
||||
centiseconds);
|
||||
setText(text.concat(centisecondsText));
|
||||
if (mApplySizeSpan) {
|
||||
SpannableString span = new SpannableString(centisecondsText);
|
||||
span.setSpan(SIZE_SPAN, 0, centisecondsText.length(),
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
setText(TextUtils.concat(text, span), BufferType.SPANNABLE);
|
||||
} else {
|
||||
setText(text.concat(centisecondsText));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRunning() {
|
||||
|
||||
@ -87,6 +87,7 @@ public class StopwatchFragment extends RecyclerViewFragment<
|
||||
// have a null reference.
|
||||
mActivityFab = new WeakReference<>((FloatingActionButton) getActivity().findViewById(R.id.fab));
|
||||
|
||||
mChronometer.setApplySizeSpan(true);
|
||||
if (mStartTime > 0) {
|
||||
long base = mStartTime;
|
||||
if (mPauseTime > 0) {
|
||||
@ -95,8 +96,11 @@ public class StopwatchFragment extends RecyclerViewFragment<
|
||||
}
|
||||
mChronometer.setBase(base);
|
||||
}
|
||||
if (mPrefs.getBoolean(KEY_CHRONOMETER_RUNNING, false)) {
|
||||
if (wasChronometerRunning()) {
|
||||
mChronometer.start();
|
||||
// Note: mChronometer.isRunning() will return false at this point and
|
||||
// in other upcoming lifecycle methods because it is not yet visible
|
||||
// (i.e. mVisible == false).
|
||||
}
|
||||
// Hides the mini fabs prematurely, so when we actually display this tab
|
||||
// they won't show even briefly at all before hiding.
|
||||
@ -115,6 +119,13 @@ public class StopwatchFragment extends RecyclerViewFragment<
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
// Every view that was in our tree is dereferenced for us.
|
||||
// The reason we can control the animator here is because members
|
||||
// are not dereferenced here, as evidenced by mStartTime and mPauseTime
|
||||
// retaining their values.
|
||||
if (mProgressAnimator != null) {
|
||||
mProgressAnimator.removeAllListeners();
|
||||
}
|
||||
Log.d(TAG, "onDestroyView()");
|
||||
Log.d(TAG, "mStartTime = " + mStartTime
|
||||
+ ", mPauseTime = " + mPauseTime);
|
||||
@ -127,17 +138,18 @@ public class StopwatchFragment extends RecyclerViewFragment<
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<LapCursor> loader, LapCursor data) {
|
||||
Log.d(TAG, "onLoadFinished()");
|
||||
super.onLoadFinished(loader, data);
|
||||
// TODO: Will manipulating the cursor's position here affect the current
|
||||
// position in the adapter? Should we make a defensive copy and manipulate
|
||||
// that copy instead?
|
||||
if (data.moveToFirst()) {
|
||||
mCurrentLap = data.getItem();
|
||||
Log.d(TAG, "Current lap ID = " + mCurrentLap.getId());
|
||||
// Log.d(TAG, "Current lap ID = " + mCurrentLap.getId());
|
||||
}
|
||||
if (data.moveToNext()) {
|
||||
mPreviousLap = data.getItem();
|
||||
Log.d(TAG, "Previous lap ID = " + mPreviousLap.getId());
|
||||
// Log.d(TAG, "Previous lap ID = " + mPreviousLap.getId());
|
||||
}
|
||||
if (mCurrentLap != null && mPreviousLap != null) {
|
||||
// We really only want to start a new animator when the NEWLY RETRIEVED current
|
||||
@ -151,7 +163,10 @@ public class StopwatchFragment extends RecyclerViewFragment<
|
||||
// have different values for, e.g., t1 and pauseTime.
|
||||
//
|
||||
// Therefore, we'll just always end the previous animator and start a new one.
|
||||
if (mChronometer.isRunning()) {
|
||||
//
|
||||
// NOTE: If we just recreated ourselves due to rotation, mChronometer.isRunning() == false,
|
||||
// because it is not yet visible (i.e. mVisible == false).
|
||||
if (mChronometer.isRunning() || wasChronometerRunning()) {
|
||||
startNewProgressBarAnimator();
|
||||
} else {
|
||||
// I verified the bar was visible already without this, so we probably don't need this,
|
||||
@ -299,41 +314,39 @@ public class StopwatchFragment extends RecyclerViewFragment<
|
||||
mSeekBar.setVisibility(View.VISIBLE);
|
||||
mProgressAnimator = ProgressBarUtils.startNewAnimator(
|
||||
mSeekBar, getCurrentLapProgressRatio(), timeRemaining);
|
||||
mProgressAnimator.addListener(mAnimatorListener);
|
||||
}
|
||||
mProgressAnimator.addListener(new Animator.AnimatorListener() {
|
||||
private boolean cancelled;
|
||||
|
||||
private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
|
||||
private boolean cancelled;
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
// Pausing the stopwatch (and the current lap) uses Animator.cancel(), which will
|
||||
// not only fire onAnimationCancel(Animator), but also onAnimationEnd(Animator).
|
||||
// We should only let this call through when actually Animator.end() was called,
|
||||
// and that happens when we stop() the stopwatch.
|
||||
// If we didn't have this check, we'd be hiding the SeekBar every time we pause
|
||||
// a lap.
|
||||
if (!cancelled) {
|
||||
mSeekBar.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
cancelled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
cancelled = true;
|
||||
}
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
// Pausing the stopwatch (and the current lap) uses Animator.cancel(), which will
|
||||
// not only fire onAnimationCancel(Animator), but also onAnimationEnd(Animator).
|
||||
// We should only let this call through when actually Animator.end() was called,
|
||||
// and that happens when we stop() the stopwatch.
|
||||
// If we didn't have this check, we'd be hiding the SeekBar every time we pause
|
||||
// a lap.
|
||||
if (!cancelled) {
|
||||
mSeekBar.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
cancelled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private double getCurrentLapProgressRatio() {
|
||||
if (mPreviousLap == null)
|
||||
@ -356,6 +369,9 @@ public class StopwatchFragment extends RecyclerViewFragment<
|
||||
.apply();
|
||||
}
|
||||
|
||||
private boolean wasChronometerRunning() {
|
||||
return mPrefs.getBoolean(KEY_CHRONOMETER_RUNNING, false);
|
||||
}
|
||||
// ======================= DO NOT IMPLEMENT ============================
|
||||
|
||||
@Override
|
||||
|
||||
@ -17,14 +17,12 @@ import com.philliphsu.clock2.util.ProgressBarUtils;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.OnClick;
|
||||
import butterknife.OnTouch;
|
||||
|
||||
/**
|
||||
* Created by Phillip Hsu on 7/25/2016.
|
||||
*/
|
||||
public class TimerViewHolder extends BaseViewHolder<Timer> {
|
||||
private static final String TAG = "TimerViewHolder";
|
||||
private static final int MAX_PROGRESS = 100000;
|
||||
|
||||
private final AsyncTimersTableUpdateHandler mAsyncTimersTableUpdateHandler;
|
||||
private TimerController mController;
|
||||
@ -71,11 +69,6 @@ public class TimerViewHolder extends BaseViewHolder<Timer> {
|
||||
mController.stop();
|
||||
}
|
||||
|
||||
@OnTouch(R.id.seek_bar)
|
||||
boolean captureTouchOnSeekBar() {
|
||||
return true; // Do nothing when the user touches the seek bar
|
||||
}
|
||||
|
||||
private void bindLabel(String label) {
|
||||
if (!label.isEmpty()) {
|
||||
mLabel.setText(label);
|
||||
@ -137,9 +130,10 @@ public class TimerViewHolder extends BaseViewHolder<Timer> {
|
||||
if (!timer.isRunning()) {
|
||||
// If our scale were 1, then casting ratio to an int will ALWAYS
|
||||
// truncate down to zero.
|
||||
mSeekBar.setMax(100);
|
||||
final int progress = (int) (100 * ratio);
|
||||
mSeekBar.setProgress(progress);
|
||||
// mSeekBar.setMax(100);
|
||||
// final int progress = (int) (100 * ratio);
|
||||
// mSeekBar.setProgress(progress);
|
||||
ProgressBarUtils.setProgress(mSeekBar, ratio);
|
||||
// mSeekBar.getThumb().mutate().setAlpha(progress == 0 ? 0 : 255);
|
||||
} else {
|
||||
// mSeekBar.getThumb().mutate().setAlpha(255);
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<SeekBar
|
||||
<com.philliphsu.clock2.UntouchableSeekBar
|
||||
android:id="@+id/seek_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@ -33,9 +33,8 @@
|
||||
<!--TODO: Consider removing this bottom margin, because the seekbar
|
||||
is rendering with HUGE top and bottom padding already. -->
|
||||
|
||||
<!-- Cannot be touch controlled -->
|
||||
<!--The default style has padding start and end, so we remove both-->
|
||||
<SeekBar
|
||||
<com.philliphsu.clock2.UntouchableSeekBar
|
||||
android:id="@+id/seek_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user