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 * @hide 33 */ 34 public class PagedScrollBarView extends FrameLayout 35 implements View.OnClickListener, View.OnLongClickListener { 36 private static final float BUTTON_DISABLED_ALPHA = 0.2f; 37 38 /** 39 * Listener for when the list should paginate. 40 */ 41 public interface PaginationListener { 42 int PAGE_UP = 0; 43 int PAGE_DOWN = 1; 44 45 /** Called when the linked view should be paged in the given direction */ 46 void onPaginate(int direction); 47 } 48 49 private final ImageView mUpButton; 50 private final ImageView mDownButton; 51 private final ImageView mScrollThumb; 52 /** The "filler" view between the up and down buttons */ 53 private final View mFiller; 54 private final Interpolator mPaginationInterpolator = new AccelerateDecelerateInterpolator(); 55 private final int mMinThumbLength; 56 private final int mMaxThumbLength; 57 private PaginationListener mPaginationListener; 58 59 public PagedScrollBarView( 60 Context context, AttributeSet attrs) { 61 this(context, attrs, 0 /*defStyleAttrs*/, 0 /*defStyleRes*/); 62 } 63 64 public PagedScrollBarView( 65 Context context, AttributeSet attrs, int defStyleAttrs) { 66 this(context, attrs, defStyleAttrs, 0 /*defStyleRes*/); 67 } 68 69 public PagedScrollBarView( 70 Context context, AttributeSet attrs, int defStyleAttrs, int defStyleRes) { 71 super(context, attrs, defStyleAttrs, defStyleRes); 72 73 LayoutInflater inflater = (LayoutInflater) context 74 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 75 inflater.inflate( 76 R.layout.car_paged_scrollbar_buttons, this /*root*/, true /*attachToRoot*/); 77 78 mUpButton = (ImageView) findViewById(R.id.page_up); 79 mUpButton.setImageDrawable(context.getDrawable(R.drawable.ic_up_button)); 80 mUpButton.setOnClickListener(this); 81 mUpButton.setOnLongClickListener(this); 82 mDownButton = (ImageView) findViewById(R.id.page_down); 83 mDownButton.setImageDrawable(context.getDrawable(R.drawable.ic_down_button)); 84 mDownButton.setOnClickListener(this); 85 mDownButton.setOnLongClickListener(this); 86 87 mScrollThumb = (ImageView) findViewById(R.id.scrollbar_thumb); 88 mScrollThumb.setAlpha(0.5f); 89 90 mFiller = findViewById(R.id.filler); 91 92 mMinThumbLength = getResources().getDimensionPixelSize(R.dimen.min_thumb_height); 93 mMaxThumbLength = getResources().getDimensionPixelSize(R.dimen.max_thumb_height); 94 95 if (!context.getResources().getBoolean(R.bool.car_true_for_touch)) { 96 // Don't show the pagination buttons if there isn't touch. 97 mUpButton.setVisibility(View.GONE); 98 mDownButton.setVisibility(View.GONE); 99 } 100 } 101 102 @Override 103 public void onClick(View v) { 104 dispatchPageClick(v); 105 } 106 107 @Override 108 public boolean onLongClick(View v) { 109 dispatchPageClick(v); 110 return true; 111 } 112 113 public void setPaginationListener(PaginationListener listener) { 114 mPaginationListener = listener; 115 } 116 117 /** Returns {@code true} if the "up" button is pressed */ 118 public boolean isUpPressed() { 119 return mUpButton.isPressed(); 120 } 121 122 /** Returns {@code true} if the "down" button is pressed */ 123 public boolean isDownPressed() { 124 return mDownButton.isPressed(); 125 } 126 127 /** Sets the range, offset and extent of the scroll bar. See {@link android.view.View}. */ 128 protected void setParameters(int range, int offset, int extent, boolean animate) { 129 final int size = mFiller.getHeight() - mFiller.getPaddingTop() - mFiller.getPaddingBottom(); 130 131 int thumbLength = extent * size / range; 132 thumbLength = Math.max(Math.min(thumbLength, mMaxThumbLength), mMinThumbLength); 133 134 int thumbOffset = size - thumbLength; 135 if (isDownEnabled()) { 136 // We need to adjust the offset so that it fits into the possible space inside the 137 // filler with regarding to the constraints set by mMaxThumbLength and mMinThumbLength. 138 thumbOffset = (size - thumbLength) * offset / range; 139 } 140 141 // Sets the size of the thumb and request a redraw if needed. 142 final ViewGroup.LayoutParams lp = mScrollThumb.getLayoutParams(); 143 if (lp.height != thumbLength) { 144 lp.height = thumbLength; 145 mScrollThumb.requestLayout(); 146 } 147 148 moveY(mScrollThumb, thumbOffset, animate); 149 } 150 151 /** Sets auto day/night mode */ 152 protected void setAutoDayNightMode() { 153 int color = getResources().getColor(R.color.car_tint); 154 mUpButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); 155 mUpButton.setBackgroundResource(R.drawable.car_pagination_background); 156 mDownButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); 157 mDownButton.setBackgroundResource(R.drawable.car_pagination_background); 158 159 mScrollThumb.setBackgroundColor(color); 160 } 161 162 /** Sets auto light mode */ 163 protected void setLightMode() { 164 int color = getResources().getColor(R.color.car_tint_light); 165 mUpButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); 166 mUpButton.setBackgroundResource(R.drawable.car_pagination_background_light); 167 mDownButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); 168 mDownButton.setBackgroundResource(R.drawable.car_pagination_background_light); 169 170 mScrollThumb.setBackgroundColor(color); 171 } 172 173 /** Sets auto dark mode */ 174 protected void setDarkMode() { 175 int color = getResources().getColor(R.color.car_tint_dark); 176 mUpButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); 177 mUpButton.setBackgroundResource(R.drawable.car_pagination_background_dark); 178 mDownButton.setColorFilter(color, PorterDuff.Mode.SRC_IN); 179 mDownButton.setBackgroundResource(R.drawable.car_pagination_background_dark); 180 181 mScrollThumb.setBackgroundColor(color); 182 } 183 184 protected void setUpEnabled(boolean enabled) { 185 mUpButton.setEnabled(enabled); 186 mUpButton.setAlpha(enabled ? 1f : BUTTON_DISABLED_ALPHA); 187 } 188 189 protected void setDownEnabled(boolean enabled) { 190 mDownButton.setEnabled(enabled); 191 mDownButton.setAlpha(enabled ? 1f : BUTTON_DISABLED_ALPHA); 192 } 193 194 protected boolean isDownEnabled() { 195 return mDownButton.isEnabled(); 196 } 197 198 private void dispatchPageClick(View v) { 199 final PaginationListener listener = mPaginationListener; 200 if (listener == null) { 201 return; 202 } 203 204 final int direction = (v.getId() == R.id.page_up) 205 ? PaginationListener.PAGE_UP : PaginationListener.PAGE_DOWN; 206 listener.onPaginate(direction); 207 } 208 209 /** Moves the given view to the specified 'y' position. */ 210 private void moveY(final View view, float newPosition, boolean animate) { 211 final int duration = animate ? 200 : 0; 212 view.animate() 213 .y(newPosition) 214 .setDuration(duration) 215 .setInterpolator(mPaginationInterpolator) 216 .start(); 217 } 218 }