1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.support.car.ui; 17 18 import android.content.Context; 19 import android.graphics.PorterDuff; 20 import android.util.AttributeSet; 21 import android.view.LayoutInflater; 22 import android.view.View; 23 import android.view.ViewGroup; 24 import android.view.animation.AccelerateDecelerateInterpolator; 25 import android.view.animation.Interpolator; 26 import android.widget.FrameLayout; 27 import android.widget.ImageView; 28 29 /** 30 * A custom view to provide list scroll behaviour -- up/down buttons and scroll indicator. 31 */ 32 public class PagedScrollBarView extends FrameLayout 33 implements View.OnClickListener, View.OnLongClickListener { 34 private static final float BUTTON_DISABLED_ALPHA = 0.2f; 35 36 /** 37 * Listener for when the list should paginate. 38 */ 39 public interface PaginationListener { 40 int PAGE_UP = 0; 41 int PAGE_DOWN = 1; 42 43 /** Called when the linked view should be paged in the given direction */ onPaginate(int direction)44 void onPaginate(int direction); 45 } 46 47 private final ImageView mUpButton; 48 private final ImageView mDownButton; 49 private final ImageView mScrollThumb; 50 /** The "filler" view between the up and down buttons */ 51 private final View mFiller; 52 private final Interpolator mPaginationInterpolator = new AccelerateDecelerateInterpolator(); 53 private final int mMinThumbLength; 54 private final int mMaxThumbLength; 55 private PaginationListener mPaginationListener; 56 PagedScrollBarView( Context context, AttributeSet attrs)57 public PagedScrollBarView( 58 Context context, AttributeSet attrs) { 59 this(context, attrs, 0 /*defStyleAttrs*/, 0 /*defStyleRes*/); 60 } 61 PagedScrollBarView( Context context, AttributeSet attrs, int defStyleAttrs)62 public PagedScrollBarView( 63 Context context, AttributeSet attrs, int defStyleAttrs) { 64 this(context, attrs, defStyleAttrs, 0 /*defStyleRes*/); 65 } 66 PagedScrollBarView( Context context, AttributeSet attrs, int defStyleAttrs, int defStyleRes)67 public PagedScrollBarView( 68 Context context, AttributeSet attrs, int defStyleAttrs, int defStyleRes) { 69 super(context, attrs, defStyleAttrs, defStyleRes); 70 71 LayoutInflater inflater = (LayoutInflater) context 72 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 73 inflater.inflate( 74 R.layout.car_paged_scrollbar_buttons, this /*root*/, true /*attachToRoot*/); 75 76 mUpButton = (ImageView) findViewById(R.id.page_up); 77 mUpButton.setImageDrawable(CarUiResourceLoader.getDrawable(context, "ic_up")); 78 mUpButton.setOnClickListener(this); 79 mUpButton.setOnLongClickListener(this); 80 mDownButton = (ImageView) findViewById(R.id.page_down); 81 mDownButton.setImageDrawable(CarUiResourceLoader.getDrawable(context, "ic_down")); 82 mDownButton.setOnClickListener(this); 83 mDownButton.setOnLongClickListener(this); 84 85 mScrollThumb = (ImageView) findViewById(R.id.scrollbar_thumb); 86 mFiller = findViewById(R.id.filler); 87 88 mMinThumbLength = getResources().getDimensionPixelSize(R.dimen.min_thumb_height); 89 mMaxThumbLength = getResources().getDimensionPixelSize(R.dimen.max_thumb_height); 90 91 if (!context.getResources().getBoolean(R.bool.car_true_for_touch)) { 92 // Don't show the pagination buttons if there isn't touch. 93 mUpButton.setVisibility(View.GONE); 94 mDownButton.setVisibility(View.GONE); 95 } 96 } 97 98 @Override onClick(View v)99 public void onClick(View v) { 100 dispatchPageClick(v); 101 } 102 103 @Override onLongClick(View v)104 public boolean onLongClick(View v) { 105 dispatchPageClick(v); 106 return true; 107 } 108 setPaginationListener(PaginationListener listener)109 public void setPaginationListener(PaginationListener listener) { 110 mPaginationListener = listener; 111 } 112 113 /** Returns {@code true} if the "up" button is pressed */ isUpPressed()114 public boolean isUpPressed() { 115 return mUpButton.isPressed(); 116 } 117 118 /** Returns {@code true} if the "down" button is pressed */ isDownPressed()119 public boolean isDownPressed() { 120 return mDownButton.isPressed(); 121 } 122 123 /** Sets the range, offset and extent of the scroll bar. See {@link android.view.View}. */ setParameters(int range, int offset, int extent, boolean animate)124 protected void setParameters(int range, int offset, int extent, boolean animate) { 125 final int size = mFiller.getHeight() - mFiller.getPaddingTop() - mFiller.getPaddingBottom(); 126 127 int thumbLength = extent * size / range; 128 thumbLength = Math.max(Math.min(thumbLength, mMaxThumbLength), mMinThumbLength); 129 130 int thumbOffset = size - thumbLength; 131 if (isDownEnabled()) { 132 // We need to adjust the offset so that it fits into the possible space inside the 133 // filler with regarding to the constraints set by mMaxThumbLength and mMinThumbLength. 134 thumbOffset = (size - thumbLength) * offset / range; 135 } 136 137 // Sets the size of the thumb and request a redraw if needed. 138 final ViewGroup.LayoutParams lp = mScrollThumb.getLayoutParams(); 139 if (lp.height != thumbLength) { 140 lp.height = thumbLength; 141 mScrollThumb.requestLayout(); 142 } 143 144 moveY(mScrollThumb, thumbOffset, animate); 145 } 146 147 /** Sets auto day/night mode */ setAutoDayNightMode()148 protected void setAutoDayNightMode() { 149 int color = getResources().getColor(R.color.car_tint); 150 mUpButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); 151 mUpButton.setBackgroundResource(R.drawable.car_pagination_background); 152 mDownButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); 153 mDownButton.setBackgroundResource(R.drawable.car_pagination_background); 154 } 155 156 /** Sets auto light mode */ setLightMode()157 protected void setLightMode() { 158 int color = getResources().getColor(R.color.car_tint_light); 159 mUpButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); 160 mUpButton.setBackgroundResource(R.drawable.car_pagination_background_light); 161 mDownButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); 162 mDownButton.setBackgroundResource(R.drawable.car_pagination_background_light); 163 } 164 165 /** Sets auto dark mode */ setDarkMode()166 protected void setDarkMode() { 167 int color = getResources().getColor(R.color.car_tint_dark); 168 mUpButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); 169 mUpButton.setBackgroundResource(R.drawable.car_pagination_background_dark); 170 mDownButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); 171 mDownButton.setBackgroundResource(R.drawable.car_pagination_background_dark); 172 } 173 setUpEnabled(boolean enabled)174 protected void setUpEnabled(boolean enabled) { 175 mUpButton.setEnabled(enabled); 176 mUpButton.setAlpha(enabled ? 1f : BUTTON_DISABLED_ALPHA); 177 } 178 setDownEnabled(boolean enabled)179 protected void setDownEnabled(boolean enabled) { 180 mDownButton.setEnabled(enabled); 181 mDownButton.setAlpha(enabled ? 1f : BUTTON_DISABLED_ALPHA); 182 } 183 isDownEnabled()184 protected boolean isDownEnabled() { 185 return mDownButton.isEnabled(); 186 } 187 dispatchPageClick(View v)188 private void dispatchPageClick(View v) { 189 final PaginationListener listener = mPaginationListener; 190 if (listener == null) { 191 return; 192 } 193 194 final int direction = (v.getId() == R.id.page_up) 195 ? PaginationListener.PAGE_UP : PaginationListener.PAGE_DOWN; 196 listener.onPaginate(direction); 197 } 198 199 /** Moves the given view to the specified 'y' position. */ moveY(final View view, float newPosition, boolean animate)200 private void moveY(final View view, float newPosition, boolean animate) { 201 final int duration = animate ? 200 : 0; 202 view.animate() 203 .y(newPosition) 204 .setDuration(duration) 205 .setInterpolator(mPaginationInterpolator) 206 .start(); 207 } 208 }