Optimize CoverView scrolling by only redrawing the dirty region
This commit is contained in:
parent
aadbf2c9e2
commit
c880907a03
@ -41,28 +41,50 @@ import android.widget.Scroller;
|
|||||||
* generated by CoverBitmap.
|
* generated by CoverBitmap.
|
||||||
*/
|
*/
|
||||||
public final class CoverView extends View implements Handler.Callback {
|
public final class CoverView extends View implements Handler.Callback {
|
||||||
|
/**
|
||||||
|
* The system-provided snap velocity, used as a threshold for detecting
|
||||||
|
* flings.
|
||||||
|
*/
|
||||||
private static int sSnapVelocity = -1;
|
private static int sSnapVelocity = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Handler with which to do background work. Will be null until
|
* The Handler with which to do background work. Will be null until
|
||||||
* setupHandler is called.
|
* setupHandler is called.
|
||||||
*/
|
*/
|
||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
|
/**
|
||||||
|
* A handler running on the UI thread, for UI operations.
|
||||||
|
*/
|
||||||
|
private final Handler mUiHandler = new Handler(this);
|
||||||
/**
|
/**
|
||||||
* How to render cover art and metadata. One of
|
* How to render cover art and metadata. One of
|
||||||
* CoverBitmap.STYLE_*
|
* CoverBitmap.STYLE_*
|
||||||
*/
|
*/
|
||||||
private int mCoverStyle;
|
private int mCoverStyle;
|
||||||
|
/**
|
||||||
|
* Interface to respond to CoverView motion actions.
|
||||||
|
*/
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
|
/**
|
||||||
|
* Called after the view has scrolled to the next (right) cover.
|
||||||
|
*/
|
||||||
public void nextSong();
|
public void nextSong();
|
||||||
|
/**
|
||||||
|
* Called after the view has scrolled to the previous (left) cover.
|
||||||
|
*/
|
||||||
public void previousSong();
|
public void previousSong();
|
||||||
|
/**
|
||||||
|
* Called when the user has swiped up on the view.
|
||||||
|
*/
|
||||||
public void upSwipe();
|
public void upSwipe();
|
||||||
|
/**
|
||||||
|
* Called when the user had swiped down on the view.
|
||||||
|
*/
|
||||||
public void downSwipe();
|
public void downSwipe();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* The instance of the callback.
|
||||||
|
*/
|
||||||
private Callback mCallback;
|
private Callback mCallback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current set of songs: 0 = previous, 1 = current, and 2 = next.
|
* The current set of songs: 0 = previous, 1 = current, and 2 = next.
|
||||||
*/
|
*/
|
||||||
@ -79,24 +101,49 @@ public final class CoverView extends View implements Handler.Callback {
|
|||||||
* Cover art to use when a song has no cover art in no info display styles.
|
* Cover art to use when a song has no cover art in no info display styles.
|
||||||
*/
|
*/
|
||||||
private Bitmap mDefaultCover;
|
private Bitmap mDefaultCover;
|
||||||
|
/**
|
||||||
|
* Computes scroll animations.
|
||||||
|
*/
|
||||||
private final Scroller mScroller;
|
private final Scroller mScroller;
|
||||||
|
/**
|
||||||
|
* Computes scroll velocity to detect flings.
|
||||||
|
*/
|
||||||
private VelocityTracker mVelocityTracker;
|
private VelocityTracker mVelocityTracker;
|
||||||
|
/**
|
||||||
|
* The x coordinate of the last touch down or move event.
|
||||||
|
*/
|
||||||
private float mLastMotionX;
|
private float mLastMotionX;
|
||||||
|
/**
|
||||||
|
* The y coordinate of the last touch down or move event.
|
||||||
|
*/
|
||||||
private float mLastMotionY;
|
private float mLastMotionY;
|
||||||
|
/**
|
||||||
|
* The x coordinate of the last touch down event.
|
||||||
|
*/
|
||||||
private float mStartX;
|
private float mStartX;
|
||||||
|
/**
|
||||||
|
* The y coordinate of the last touch down event.
|
||||||
|
*/
|
||||||
private float mStartY;
|
private float mStartY;
|
||||||
|
/**
|
||||||
|
* The index of the cover that is being scrolled to during a fling
|
||||||
|
* animation, or -1 if the cover is the active (middle) cover.
|
||||||
|
*/
|
||||||
private int mTentativeCover = -1;
|
private int mTentativeCover = -1;
|
||||||
/**
|
/**
|
||||||
* Ignore the next pointer up event, for long presses.
|
* Ignore the next pointer up event, for long presses.
|
||||||
*/
|
*/
|
||||||
private boolean mIgnoreNextUp;
|
private boolean mIgnoreNextUp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If true, querySongs was called before the view initialized and should
|
* If true, querySongs was called before the view initialized and should
|
||||||
* be called when initialization finishes.
|
* be called when initialization finishes.
|
||||||
*/
|
*/
|
||||||
private boolean mPendingQuery;
|
private boolean mPendingQuery;
|
||||||
|
/**
|
||||||
|
* If true, calls to invalidate() will do nothing. We use this so we can
|
||||||
|
* only invalidate the dirty rect during scrolling.
|
||||||
|
*/
|
||||||
|
private boolean mSuppressInvalidate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor intended to be called by inflating from XML.
|
* Constructor intended to be called by inflating from XML.
|
||||||
@ -151,21 +198,20 @@ public final class CoverView extends View implements Handler.Callback {
|
|||||||
@Override
|
@Override
|
||||||
protected void onDraw(Canvas canvas)
|
protected void onDraw(Canvas canvas)
|
||||||
{
|
{
|
||||||
super.onDraw(canvas);
|
|
||||||
|
|
||||||
int width = getWidth();
|
int width = getWidth();
|
||||||
int height = getHeight();
|
int height = getHeight();
|
||||||
|
int x = 0;
|
||||||
int scrollX = getScrollX();
|
int scrollX = getScrollX();
|
||||||
|
|
||||||
canvas.drawColor(Color.BLACK);
|
canvas.drawColor(Color.BLACK);
|
||||||
|
|
||||||
for (int x = 0, i = 0; i != 3; ++i, x += width) {
|
for (Bitmap bitmap : mBitmaps) {
|
||||||
Bitmap bitmap = mBitmaps[i];
|
|
||||||
if (bitmap != null && scrollX + width > x && scrollX < x + width) {
|
if (bitmap != null && scrollX + width > x && scrollX < x + width) {
|
||||||
int xOffset = (width - bitmap.getWidth()) / 2;
|
int xOffset = (width - bitmap.getWidth()) / 2;
|
||||||
int yOffset = (height - bitmap.getHeight()) / 2;
|
int yOffset = (height - bitmap.getHeight()) / 2;
|
||||||
canvas.drawBitmap(bitmap, x + xOffset, yOffset, null);
|
canvas.drawBitmap(bitmap, x + xOffset, yOffset, null);
|
||||||
}
|
}
|
||||||
|
x += width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +244,7 @@ public final class CoverView extends View implements Handler.Callback {
|
|||||||
mLastMotionX = x;
|
mLastMotionX = x;
|
||||||
mLastMotionY = y;
|
mLastMotionY = y;
|
||||||
|
|
||||||
mHandler.sendEmptyMessageDelayed(MSG_LONG_CLICK, ViewConfiguration.getLongPressTimeout());
|
mUiHandler.sendEmptyMessageDelayed(MSG_LONG_CLICK, ViewConfiguration.getLongPressTimeout());
|
||||||
break;
|
break;
|
||||||
case MotionEvent.ACTION_MOVE: {
|
case MotionEvent.ACTION_MOVE: {
|
||||||
float deltaX = mLastMotionX - x;
|
float deltaX = mLastMotionX - x;
|
||||||
@ -221,7 +267,7 @@ public final class CoverView extends View implements Handler.Callback {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MotionEvent.ACTION_UP: {
|
case MotionEvent.ACTION_UP: {
|
||||||
mHandler.removeMessages(MSG_LONG_CLICK);
|
mUiHandler.removeMessages(MSG_LONG_CLICK);
|
||||||
|
|
||||||
VelocityTracker velocityTracker = mVelocityTracker;
|
VelocityTracker velocityTracker = mVelocityTracker;
|
||||||
velocityTracker.computeCurrentVelocity(250);
|
velocityTracker.computeCurrentVelocity(250);
|
||||||
@ -266,7 +312,7 @@ public final class CoverView extends View implements Handler.Callback {
|
|||||||
if (whichCover != 1)
|
if (whichCover != 1)
|
||||||
mTentativeCover = whichCover;
|
mTentativeCover = whichCover;
|
||||||
|
|
||||||
postInvalidate();
|
mUiHandler.sendEmptyMessage(MSG_SCROLL);
|
||||||
|
|
||||||
if (mVelocityTracker != null) {
|
if (mVelocityTracker != null) {
|
||||||
mVelocityTracker.recycle();
|
mVelocityTracker.recycle();
|
||||||
@ -279,28 +325,6 @@ public final class CoverView extends View implements Handler.Callback {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update position for fling scroll animation and, when it is finished,
|
|
||||||
* notify PlaybackService that the user has requested a track change and
|
|
||||||
* update the cover art views.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void computeScroll()
|
|
||||||
{
|
|
||||||
if (mScroller.computeScrollOffset()) {
|
|
||||||
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
|
|
||||||
postInvalidate();
|
|
||||||
} else if (mTentativeCover != -1) {
|
|
||||||
int delta = mTentativeCover - 1;
|
|
||||||
mTentativeCover = -1;
|
|
||||||
if (delta == 1)
|
|
||||||
mCallback.nextSong();
|
|
||||||
else
|
|
||||||
mCallback.previousSong();
|
|
||||||
resetScroll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a bitmap for the given song if the cache does not contain one
|
* Generates a bitmap for the given song if the cache does not contain one
|
||||||
* for it, or moves the bitmap to the top of the cache if it does.
|
* for it, or moves the bitmap to the top of the cache if it does.
|
||||||
@ -390,6 +414,13 @@ public final class CoverView extends View implements Handler.Callback {
|
|||||||
* @see View#performLongClick()
|
* @see View#performLongClick()
|
||||||
*/
|
*/
|
||||||
private static final int MSG_LONG_CLICK = 2;
|
private static final int MSG_LONG_CLICK = 2;
|
||||||
|
/**
|
||||||
|
* Update position for fling scroll animation and, when it is finished,
|
||||||
|
* notify PlaybackService that the user has requested a track change and
|
||||||
|
* update the cover art views. Will resend message until scrolling is
|
||||||
|
* finished.
|
||||||
|
*/
|
||||||
|
private static final int MSG_SCROLL = 3;
|
||||||
|
|
||||||
public boolean handleMessage(Message message)
|
public boolean handleMessage(Message message)
|
||||||
{
|
{
|
||||||
@ -403,6 +434,27 @@ public final class CoverView extends View implements Handler.Callback {
|
|||||||
performLongClick();
|
performLongClick();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MSG_SCROLL:
|
||||||
|
if (mScroller.computeScrollOffset()) {
|
||||||
|
// scrollTo calls invalidate(), however, we want to invalidate
|
||||||
|
// only the region where the covers are drawn, so we need to
|
||||||
|
// suppress this call.
|
||||||
|
mSuppressInvalidate = true;
|
||||||
|
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
|
||||||
|
mSuppressInvalidate = false;
|
||||||
|
|
||||||
|
invalidateCovers();
|
||||||
|
mUiHandler.sendEmptyMessage(MSG_SCROLL);
|
||||||
|
} else if (mTentativeCover != -1) {
|
||||||
|
int delta = mTentativeCover - 1;
|
||||||
|
mTentativeCover = -1;
|
||||||
|
if (delta == 1)
|
||||||
|
mCallback.nextSong();
|
||||||
|
else
|
||||||
|
mCallback.previousSong();
|
||||||
|
resetScroll();
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -430,4 +482,40 @@ public final class CoverView extends View implements Handler.Callback {
|
|||||||
setMeasuredDimension(size, size);
|
setMeasuredDimension(size, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden to allow redraws to be suppressed.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void invalidate()
|
||||||
|
{
|
||||||
|
if (!mSuppressInvalidate)
|
||||||
|
super.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call {@link View#invalidate(int,int,int,int)} with the area
|
||||||
|
* containing the visible cover(s).
|
||||||
|
*/
|
||||||
|
public void invalidateCovers()
|
||||||
|
{
|
||||||
|
int width = getWidth();
|
||||||
|
int height = getHeight();
|
||||||
|
int scrollX = getScrollX();
|
||||||
|
int x = 0;
|
||||||
|
int maxHeight = 0;
|
||||||
|
|
||||||
|
for (Bitmap bitmap : mBitmaps) {
|
||||||
|
if (bitmap != null && scrollX + width > x && scrollX < x + width) {
|
||||||
|
int bitmapHeight = bitmap.getHeight();
|
||||||
|
if (bitmapHeight > maxHeight) {
|
||||||
|
maxHeight = bitmapHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x += width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = (height - maxHeight) / 2;
|
||||||
|
invalidate(scrollX, offset, scrollX + width, height - offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user