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