| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.widget; |
| |
| import android.annotation.NonNull; |
| import android.annotation.UnsupportedAppUsage; |
| import android.graphics.Canvas; |
| import android.graphics.ColorFilter; |
| import android.graphics.PixelFormat; |
| import android.graphics.Rect; |
| import android.graphics.drawable.Drawable; |
| |
| import com.android.internal.widget.ScrollBarUtils; |
| |
| /** |
| * This is only used by View for displaying its scroll bars. It should probably |
| * be moved in to the view package since it is used in that lower-level layer. |
| * For now, we'll hide it so it can be cleaned up later. |
| * |
| * {@hide} |
| */ |
| public class ScrollBarDrawable extends Drawable implements Drawable.Callback { |
| private Drawable mVerticalTrack; |
| private Drawable mHorizontalTrack; |
| @UnsupportedAppUsage |
| private Drawable mVerticalThumb; |
| private Drawable mHorizontalThumb; |
| |
| private int mRange; |
| private int mOffset; |
| private int mExtent; |
| |
| private boolean mVertical; |
| private boolean mBoundsChanged; |
| private boolean mRangeChanged; |
| private boolean mAlwaysDrawHorizontalTrack; |
| private boolean mAlwaysDrawVerticalTrack; |
| private boolean mMutated; |
| |
| private int mAlpha = 255; |
| private boolean mHasSetAlpha; |
| |
| private ColorFilter mColorFilter; |
| private boolean mHasSetColorFilter; |
| |
| /** |
| * Indicate whether the horizontal scrollbar track should always be drawn |
| * regardless of the extent. Defaults to false. |
| * |
| * @param alwaysDrawTrack Whether the track should always be drawn |
| * |
| * @see #getAlwaysDrawHorizontalTrack() |
| */ |
| public void setAlwaysDrawHorizontalTrack(boolean alwaysDrawTrack) { |
| mAlwaysDrawHorizontalTrack = alwaysDrawTrack; |
| } |
| |
| /** |
| * Indicate whether the vertical scrollbar track should always be drawn |
| * regardless of the extent. Defaults to false. |
| * |
| * @param alwaysDrawTrack Whether the track should always be drawn |
| * |
| * @see #getAlwaysDrawVerticalTrack() |
| */ |
| public void setAlwaysDrawVerticalTrack(boolean alwaysDrawTrack) { |
| mAlwaysDrawVerticalTrack = alwaysDrawTrack; |
| } |
| |
| /** |
| * @return whether the vertical scrollbar track should always be drawn |
| * regardless of the extent. |
| * |
| * @see #setAlwaysDrawVerticalTrack(boolean) |
| */ |
| public boolean getAlwaysDrawVerticalTrack() { |
| return mAlwaysDrawVerticalTrack; |
| } |
| |
| /** |
| * @return whether the horizontal scrollbar track should always be drawn |
| * regardless of the extent. |
| * |
| * @see #setAlwaysDrawHorizontalTrack(boolean) |
| */ |
| public boolean getAlwaysDrawHorizontalTrack() { |
| return mAlwaysDrawHorizontalTrack; |
| } |
| |
| public void setParameters(int range, int offset, int extent, boolean vertical) { |
| if (mVertical != vertical) { |
| mVertical = vertical; |
| |
| mBoundsChanged = true; |
| } |
| |
| if (mRange != range || mOffset != offset || mExtent != extent) { |
| mRange = range; |
| mOffset = offset; |
| mExtent = extent; |
| |
| mRangeChanged = true; |
| } |
| } |
| |
| @Override |
| public void draw(Canvas canvas) { |
| final boolean vertical = mVertical; |
| final int extent = mExtent; |
| final int range = mRange; |
| |
| boolean drawTrack = true; |
| boolean drawThumb = true; |
| if (extent <= 0 || range <= extent) { |
| drawTrack = vertical ? mAlwaysDrawVerticalTrack : mAlwaysDrawHorizontalTrack; |
| drawThumb = false; |
| } |
| |
| final Rect r = getBounds(); |
| if (canvas.quickReject(r.left, r.top, r.right, r.bottom, Canvas.EdgeType.AA)) { |
| return; |
| } |
| |
| if (drawTrack) { |
| drawTrack(canvas, r, vertical); |
| } |
| |
| if (drawThumb) { |
| final int scrollBarLength = vertical ? r.height() : r.width(); |
| final int thickness = vertical ? r.width() : r.height(); |
| final int thumbLength = |
| ScrollBarUtils.getThumbLength(scrollBarLength, thickness, extent, range); |
| final int thumbOffset = |
| ScrollBarUtils.getThumbOffset(scrollBarLength, thumbLength, extent, range, |
| mOffset); |
| |
| drawThumb(canvas, r, thumbOffset, thumbLength, vertical); |
| } |
| } |
| |
| @Override |
| protected void onBoundsChange(Rect bounds) { |
| super.onBoundsChange(bounds); |
| mBoundsChanged = true; |
| } |
| |
| @Override |
| public boolean isStateful() { |
| return (mVerticalTrack != null && mVerticalTrack.isStateful()) |
| || (mVerticalThumb != null && mVerticalThumb.isStateful()) |
| || (mHorizontalTrack != null && mHorizontalTrack.isStateful()) |
| || (mHorizontalThumb != null && mHorizontalThumb.isStateful()) |
| || super.isStateful(); |
| } |
| |
| @Override |
| protected boolean onStateChange(int[] state) { |
| boolean changed = super.onStateChange(state); |
| if (mVerticalTrack != null) { |
| changed |= mVerticalTrack.setState(state); |
| } |
| if (mVerticalThumb != null) { |
| changed |= mVerticalThumb.setState(state); |
| } |
| if (mHorizontalTrack != null) { |
| changed |= mHorizontalTrack.setState(state); |
| } |
| if (mHorizontalThumb != null) { |
| changed |= mHorizontalThumb.setState(state); |
| } |
| return changed; |
| } |
| |
| private void drawTrack(Canvas canvas, Rect bounds, boolean vertical) { |
| final Drawable track; |
| if (vertical) { |
| track = mVerticalTrack; |
| } else { |
| track = mHorizontalTrack; |
| } |
| |
| if (track != null) { |
| if (mBoundsChanged) { |
| track.setBounds(bounds); |
| } |
| track.draw(canvas); |
| } |
| } |
| |
| private void drawThumb(Canvas canvas, Rect bounds, int offset, int length, boolean vertical) { |
| final boolean changed = mRangeChanged || mBoundsChanged; |
| if (vertical) { |
| if (mVerticalThumb != null) { |
| final Drawable thumb = mVerticalThumb; |
| if (changed) { |
| thumb.setBounds(bounds.left, bounds.top + offset, |
| bounds.right, bounds.top + offset + length); |
| } |
| |
| thumb.draw(canvas); |
| } |
| } else { |
| if (mHorizontalThumb != null) { |
| final Drawable thumb = mHorizontalThumb; |
| if (changed) { |
| thumb.setBounds(bounds.left + offset, bounds.top, |
| bounds.left + offset + length, bounds.bottom); |
| } |
| |
| thumb.draw(canvas); |
| } |
| } |
| } |
| |
| @UnsupportedAppUsage |
| public void setVerticalThumbDrawable(Drawable thumb) { |
| if (mVerticalThumb != null) { |
| mVerticalThumb.setCallback(null); |
| } |
| |
| propagateCurrentState(thumb); |
| mVerticalThumb = thumb; |
| } |
| |
| public void setVerticalTrackDrawable(Drawable track) { |
| if (mVerticalTrack != null) { |
| mVerticalTrack.setCallback(null); |
| } |
| |
| propagateCurrentState(track); |
| mVerticalTrack = track; |
| } |
| |
| @UnsupportedAppUsage |
| public void setHorizontalThumbDrawable(Drawable thumb) { |
| if (mHorizontalThumb != null) { |
| mHorizontalThumb.setCallback(null); |
| } |
| |
| propagateCurrentState(thumb); |
| mHorizontalThumb = thumb; |
| } |
| |
| public void setHorizontalTrackDrawable(Drawable track) { |
| if (mHorizontalTrack != null) { |
| mHorizontalTrack.setCallback(null); |
| } |
| |
| propagateCurrentState(track); |
| mHorizontalTrack = track; |
| } |
| |
| private void propagateCurrentState(Drawable d) { |
| if (d != null) { |
| if (mMutated) { |
| d.mutate(); |
| } |
| |
| d.setState(getState()); |
| d.setCallback(this); |
| |
| if (mHasSetAlpha) { |
| d.setAlpha(mAlpha); |
| } |
| |
| if (mHasSetColorFilter) { |
| d.setColorFilter(mColorFilter); |
| } |
| } |
| } |
| |
| public int getSize(boolean vertical) { |
| if (vertical) { |
| return mVerticalTrack != null ? mVerticalTrack.getIntrinsicWidth() : |
| mVerticalThumb != null ? mVerticalThumb.getIntrinsicWidth() : 0; |
| } else { |
| return mHorizontalTrack != null ? mHorizontalTrack.getIntrinsicHeight() : |
| mHorizontalThumb != null ? mHorizontalThumb.getIntrinsicHeight() : 0; |
| } |
| } |
| |
| @Override |
| public ScrollBarDrawable mutate() { |
| if (!mMutated && super.mutate() == this) { |
| if (mVerticalTrack != null) { |
| mVerticalTrack.mutate(); |
| } |
| if (mVerticalThumb != null) { |
| mVerticalThumb.mutate(); |
| } |
| if (mHorizontalTrack != null) { |
| mHorizontalTrack.mutate(); |
| } |
| if (mHorizontalThumb != null) { |
| mHorizontalThumb.mutate(); |
| } |
| mMutated = true; |
| } |
| return this; |
| } |
| |
| @Override |
| public void setAlpha(int alpha) { |
| mAlpha = alpha; |
| mHasSetAlpha = true; |
| |
| if (mVerticalTrack != null) { |
| mVerticalTrack.setAlpha(alpha); |
| } |
| if (mVerticalThumb != null) { |
| mVerticalThumb.setAlpha(alpha); |
| } |
| if (mHorizontalTrack != null) { |
| mHorizontalTrack.setAlpha(alpha); |
| } |
| if (mHorizontalThumb != null) { |
| mHorizontalThumb.setAlpha(alpha); |
| } |
| } |
| |
| @Override |
| public int getAlpha() { |
| return mAlpha; |
| } |
| |
| @Override |
| public void setColorFilter(ColorFilter colorFilter) { |
| mColorFilter = colorFilter; |
| mHasSetColorFilter = true; |
| |
| if (mVerticalTrack != null) { |
| mVerticalTrack.setColorFilter(colorFilter); |
| } |
| if (mVerticalThumb != null) { |
| mVerticalThumb.setColorFilter(colorFilter); |
| } |
| if (mHorizontalTrack != null) { |
| mHorizontalTrack.setColorFilter(colorFilter); |
| } |
| if (mHorizontalThumb != null) { |
| mHorizontalThumb.setColorFilter(colorFilter); |
| } |
| } |
| |
| @Override |
| public ColorFilter getColorFilter() { |
| return mColorFilter; |
| } |
| |
| @Override |
| public int getOpacity() { |
| return PixelFormat.TRANSLUCENT; |
| } |
| |
| @Override |
| public void invalidateDrawable(@NonNull Drawable who) { |
| invalidateSelf(); |
| } |
| |
| @Override |
| public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { |
| scheduleSelf(what, when); |
| } |
| |
| @Override |
| public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { |
| unscheduleSelf(what); |
| } |
| |
| @Override |
| public String toString() { |
| return "ScrollBarDrawable: range=" + mRange + " offset=" + mOffset + |
| " extent=" + mExtent + (mVertical ? " V" : " H"); |
| } |
| } |
| |
| |