1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package androidx.leanback.widget.picker; 16 17 import android.content.Context; 18 import android.graphics.Rect; 19 import android.text.TextUtils; 20 import android.util.AttributeSet; 21 import android.util.TypedValue; 22 import android.view.KeyEvent; 23 import android.view.LayoutInflater; 24 import android.view.View; 25 import android.view.ViewGroup; 26 import android.view.animation.AccelerateInterpolator; 27 import android.view.animation.DecelerateInterpolator; 28 import android.view.animation.Interpolator; 29 import android.widget.FrameLayout; 30 import android.widget.TextView; 31 32 import androidx.leanback.R; 33 import androidx.leanback.widget.OnChildViewHolderSelectedListener; 34 import androidx.leanback.widget.VerticalGridView; 35 import androidx.recyclerview.widget.RecyclerView; 36 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.List; 40 41 /** 42 * Picker is a widget showing multiple customized {@link PickerColumn}s. The PickerColumns are 43 * initialized in {@link #setColumns(List)}. Call {@link #setColumnAt(int, PickerColumn)} if the 44 * column value range or labels change. Call {@link #setColumnValue(int, int, boolean)} to update 45 * the current value of PickerColumn. 46 * <p> 47 * Picker has two states and will change height: 48 * <li>{@link #isActivated()} is true: Picker shows typically three items vertically (see 49 * {@link #getActivatedVisibleItemCount()}}. Columns other than {@link #getSelectedColumn()} still 50 * shows one item if the Picker is focused. On a touch screen device, the Picker will not get focus 51 * so it always show three items on all columns. On a non-touch device (a TV), the Picker will show 52 * three items only on currently activated column. If the Picker has focus, it will intercept DPAD 53 * directions and select activated column. 54 * <li>{@link #isActivated()} is false: Picker shows one item vertically (see 55 * {@link #getVisibleItemCount()}) on all columns. The size of Picker shrinks. 56 */ 57 public class Picker extends FrameLayout { 58 59 public interface PickerValueListener { 60 public void onValueChanged(Picker picker, int column); 61 } 62 63 private ViewGroup mRootView; 64 private ViewGroup mPickerView; 65 final List<VerticalGridView> mColumnViews = new ArrayList<VerticalGridView>(); 66 ArrayList<PickerColumn> mColumns; 67 68 private float mUnfocusedAlpha; 69 private float mFocusedAlpha; 70 private float mVisibleColumnAlpha; 71 private float mInvisibleColumnAlpha; 72 private int mAlphaAnimDuration; 73 private Interpolator mDecelerateInterpolator; 74 private Interpolator mAccelerateInterpolator; 75 private ArrayList<PickerValueListener> mListeners; 76 private float mVisibleItemsActivated = 3; 77 private float mVisibleItems = 1; 78 private int mSelectedColumn = 0; 79 80 private List<CharSequence> mSeparators = new ArrayList<>(); 81 private int mPickerItemLayoutId = R.layout.lb_picker_item; 82 private int mPickerItemTextViewId = 0; 83 84 /** 85 * Gets separator string between columns. 86 * 87 * @return The separator that will be populated between all the Picker columns. 88 * @deprecated Use {@link #getSeparators()} 89 */ 90 public final CharSequence getSeparator() { 91 return mSeparators.get(0); 92 } 93 94 /** 95 * Sets separator String between Picker columns. 96 * 97 * @param separator Separator String between Picker columns. 98 */ 99 public final void setSeparator(CharSequence separator) { 100 setSeparators(Arrays.asList(separator)); 101 } 102 103 /** 104 * Returns the list of separators that will be populated between the picker column fields. 105 * 106 * @return The list of separators populated between the picker column fields. 107 */ 108 public final List<CharSequence> getSeparators() { 109 return mSeparators; 110 } 111 112 /** 113 * Sets the list of separators that will be populated between the Picker columns. The 114 * number of the separators should be either 1 indicating the same separator used between all 115 * the columns fields (and nothing will be placed before the first and after the last column), 116 * or must be one unit larger than the number of columns passed to {@link #setColumns(List)}. 117 * In the latter case, the list of separators corresponds to the positions before the first 118 * column all the way to the position after the last column. 119 * An empty string for a given position indicates no separators needs to be placed for that 120 * position, otherwise a TextView with the given String will be created and placed there. 121 * 122 * @param separators The list of separators to be populated between the Picker columns. 123 */ 124 public final void setSeparators(List<CharSequence> separators) { 125 mSeparators.clear(); 126 mSeparators.addAll(separators); 127 } 128 129 /** 130 * Classes extending {@link Picker} can choose to override this method to 131 * supply the {@link Picker}'s item's layout id 132 */ 133 public final int getPickerItemLayoutId() { 134 return mPickerItemLayoutId; 135 } 136 137 /** 138 * Returns the {@link Picker}'s item's {@link TextView}'s id from within the 139 * layout provided by {@link Picker#getPickerItemLayoutId()} or 0 if the 140 * layout provided by {@link Picker#getPickerItemLayoutId()} is a {link 141 * TextView}. 142 */ 143 public final int getPickerItemTextViewId() { 144 return mPickerItemTextViewId; 145 } 146 147 /** 148 * Sets the {@link Picker}'s item's {@link TextView}'s id from within the 149 * layout provided by {@link Picker#getPickerItemLayoutId()} or 0 if the 150 * layout provided by {@link Picker#getPickerItemLayoutId()} is a {link 151 * TextView}. 152 * 153 * @param textViewId View id of TextView inside a Picker item, or 0 if the Picker item is a 154 * TextView. 155 */ 156 public final void setPickerItemTextViewId(int textViewId) { 157 mPickerItemTextViewId = textViewId; 158 } 159 160 /** 161 * Creates a Picker widget. 162 */ 163 public Picker(Context context, AttributeSet attrs, int defStyleAttr) { 164 super(context, attrs, defStyleAttr); 165 // Make it enabled and clickable to receive Click event. 166 setEnabled(true); 167 setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); 168 169 mFocusedAlpha = 1f; //getFloat(R.dimen.list_item_selected_title_text_alpha); 170 mUnfocusedAlpha = 1f; //getFloat(R.dimen.list_item_unselected_text_alpha); 171 mVisibleColumnAlpha = 0.5f; //getFloat(R.dimen.picker_item_visible_column_item_alpha); 172 mInvisibleColumnAlpha = 0f; //getFloat(R.dimen.picker_item_invisible_column_item_alpha); 173 174 mAlphaAnimDuration = 175 200; // mContext.getResources().getInteger(R.integer.dialog_animation_duration); 176 177 mDecelerateInterpolator = new DecelerateInterpolator(2.5F); 178 mAccelerateInterpolator = new AccelerateInterpolator(2.5F); 179 180 LayoutInflater inflater = LayoutInflater.from(getContext()); 181 mRootView = (ViewGroup) inflater.inflate(R.layout.lb_picker, this, true); 182 mPickerView = (ViewGroup) mRootView.findViewById(R.id.picker); 183 } 184 185 /** 186 * Get nth PickerColumn. 187 * 188 * @param colIndex Index of PickerColumn. 189 * @return PickerColumn at colIndex or null if {@link #setColumns(List)} is not called yet. 190 */ 191 public PickerColumn getColumnAt(int colIndex) { 192 if (mColumns == null) { 193 return null; 194 } 195 return mColumns.get(colIndex); 196 } 197 198 /** 199 * Get number of PickerColumns. 200 * 201 * @return Number of PickerColumns or 0 if {@link #setColumns(List)} is not called yet. 202 */ 203 public int getColumnsCount() { 204 if (mColumns == null) { 205 return 0; 206 } 207 return mColumns.size(); 208 } 209 210 /** 211 * Set columns and create Views. 212 * 213 * @param columns The actual focusable columns of a picker which are scrollable if the field 214 * takes more than one value (e.g. for a DatePicker, day, month, and year fields 215 * and for TimePicker, hour, minute, and am/pm fields form the columns). 216 */ 217 public void setColumns(List<PickerColumn> columns) { 218 if (mSeparators.size() == 0) { 219 throw new IllegalStateException("Separators size is: " + mSeparators.size() 220 + ". At least one separator must be provided"); 221 } else if (mSeparators.size() == 1) { 222 CharSequence separator = mSeparators.get(0); 223 mSeparators.clear(); 224 mSeparators.add(""); 225 for (int i = 0; i < columns.size() - 1; i++) { 226 mSeparators.add(separator); 227 } 228 mSeparators.add(""); 229 } else { 230 if (mSeparators.size() != (columns.size() + 1)) { 231 throw new IllegalStateException("Separators size: " + mSeparators.size() + " must" 232 + "equal the size of columns: " + columns.size() + " + 1"); 233 } 234 } 235 236 mColumnViews.clear(); 237 mPickerView.removeAllViews(); 238 mColumns = new ArrayList<PickerColumn>(columns); 239 if (mSelectedColumn > mColumns.size() - 1) { 240 mSelectedColumn = mColumns.size() - 1; 241 } 242 LayoutInflater inflater = LayoutInflater.from(getContext()); 243 int totalCol = getColumnsCount(); 244 245 if (!TextUtils.isEmpty(mSeparators.get(0))) { 246 TextView separator = (TextView) inflater.inflate( 247 R.layout.lb_picker_separator, mPickerView, false); 248 separator.setText(mSeparators.get(0)); 249 mPickerView.addView(separator); 250 } 251 for (int i = 0; i < totalCol; i++) { 252 final int colIndex = i; 253 final VerticalGridView columnView = (VerticalGridView) inflater.inflate( 254 R.layout.lb_picker_column, mPickerView, false); 255 // we don't want VerticalGridView to receive focus. 256 updateColumnSize(columnView); 257 // always center aligned, not aligning selected item on top/bottom edge. 258 columnView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE); 259 // Width is dynamic, so has fixed size is false. 260 columnView.setHasFixedSize(false); 261 columnView.setFocusable(isActivated()); 262 // Setting cache size to zero in order to rebind item views when picker widget becomes 263 // activated. Rebinding is necessary to update the alphas when the columns are expanded 264 // as a result of the picker getting activated, otherwise the cached views with the 265 // wrong alphas could be laid out. 266 columnView.setItemViewCacheSize(0); 267 268 mColumnViews.add(columnView); 269 // add view to root 270 mPickerView.addView(columnView); 271 272 if (!TextUtils.isEmpty(mSeparators.get(i + 1))) { 273 // add a separator if not the last element 274 TextView separator = (TextView) inflater.inflate( 275 R.layout.lb_picker_separator, mPickerView, false); 276 separator.setText(mSeparators.get(i + 1)); 277 mPickerView.addView(separator); 278 } 279 280 columnView.setAdapter(new PickerScrollArrayAdapter(getContext(), 281 getPickerItemLayoutId(), getPickerItemTextViewId(), colIndex)); 282 columnView.setOnChildViewHolderSelectedListener(mColumnChangeListener); 283 } 284 } 285 286 /** 287 * When column labels change or column range changes, call this function to re-populate the 288 * selection list. Note this function cannot be called from RecyclerView layout/scroll pass. 289 * 290 * @param columnIndex Index of column to update. 291 * @param column New column to update. 292 */ 293 public void setColumnAt(int columnIndex, PickerColumn column) { 294 mColumns.set(columnIndex, column); 295 VerticalGridView columnView = mColumnViews.get(columnIndex); 296 PickerScrollArrayAdapter adapter = (PickerScrollArrayAdapter) columnView.getAdapter(); 297 if (adapter != null) { 298 adapter.notifyDataSetChanged(); 299 } 300 columnView.setSelectedPosition(column.getCurrentValue() - column.getMinValue()); 301 } 302 303 /** 304 * Manually set current value of a column. The function will update UI and notify listeners. 305 * 306 * @param columnIndex Index of column to update. 307 * @param value New value of the column. 308 * @param runAnimation True to scroll to the value or false otherwise. 309 */ 310 public void setColumnValue(int columnIndex, int value, boolean runAnimation) { 311 PickerColumn column = mColumns.get(columnIndex); 312 if (column.getCurrentValue() != value) { 313 column.setCurrentValue(value); 314 notifyValueChanged(columnIndex); 315 VerticalGridView columnView = mColumnViews.get(columnIndex); 316 if (columnView != null) { 317 int position = value - mColumns.get(columnIndex).getMinValue(); 318 if (runAnimation) { 319 columnView.setSelectedPositionSmooth(position); 320 } else { 321 columnView.setSelectedPosition(position); 322 } 323 } 324 } 325 } 326 327 private void notifyValueChanged(int columnIndex) { 328 if (mListeners != null) { 329 for (int i = mListeners.size() - 1; i >= 0; i--) { 330 mListeners.get(i).onValueChanged(this, columnIndex); 331 } 332 } 333 } 334 335 /** 336 * Register a callback to be invoked when the picker's value has changed. 337 * 338 * @param listener The callback to ad 339 */ 340 public void addOnValueChangedListener(PickerValueListener listener) { 341 if (mListeners == null) { 342 mListeners = new ArrayList<Picker.PickerValueListener>(); 343 } 344 mListeners.add(listener); 345 } 346 347 /** 348 * Remove a previously installed value changed callback 349 * 350 * @param listener The callback to remove. 351 */ 352 public void removeOnValueChangedListener(PickerValueListener listener) { 353 if (mListeners != null) { 354 mListeners.remove(listener); 355 } 356 } 357 358 void updateColumnAlpha(int colIndex, boolean animate) { 359 VerticalGridView column = mColumnViews.get(colIndex); 360 361 int selected = column.getSelectedPosition(); 362 View item; 363 364 for (int i = 0; i < column.getAdapter().getItemCount(); i++) { 365 item = column.getLayoutManager().findViewByPosition(i); 366 if (item != null) { 367 setOrAnimateAlpha(item, (selected == i), colIndex, animate); 368 } 369 } 370 } 371 372 void setOrAnimateAlpha(View view, boolean selected, int colIndex, 373 boolean animate) { 374 boolean columnShownAsActivated = colIndex == mSelectedColumn || !hasFocus(); 375 if (selected) { 376 // set alpha for main item (selected) in the column 377 if (columnShownAsActivated) { 378 setOrAnimateAlpha(view, animate, mFocusedAlpha, -1, mDecelerateInterpolator); 379 } else { 380 setOrAnimateAlpha(view, animate, mUnfocusedAlpha, -1, mDecelerateInterpolator); 381 } 382 } else { 383 // set alpha for remaining items in the column 384 if (columnShownAsActivated) { 385 setOrAnimateAlpha(view, animate, mVisibleColumnAlpha, -1, mDecelerateInterpolator); 386 } else { 387 setOrAnimateAlpha(view, animate, mInvisibleColumnAlpha, -1, 388 mDecelerateInterpolator); 389 } 390 } 391 } 392 393 private void setOrAnimateAlpha(View view, boolean animate, float destAlpha, float startAlpha, 394 Interpolator interpolator) { 395 view.animate().cancel(); 396 if (!animate) { 397 view.setAlpha(destAlpha); 398 } else { 399 if (startAlpha >= 0.0f) { 400 // set a start alpha 401 view.setAlpha(startAlpha); 402 } 403 view.animate().alpha(destAlpha) 404 .setDuration(mAlphaAnimDuration).setInterpolator(interpolator) 405 .start(); 406 } 407 } 408 409 /** 410 * Classes extending {@link Picker} can override this function to supply the 411 * behavior when a list has been scrolled. Subclass may call {@link #setColumnValue(int, int, 412 * boolean)} and or {@link #setColumnAt(int, PickerColumn)}. Subclass should not directly call 413 * {@link PickerColumn#setCurrentValue(int)} which does not update internal state or notify 414 * listeners. 415 * 416 * @param columnIndex index of which column was changed. 417 * @param newValue A new value desired to be set on the column. 418 */ 419 public void onColumnValueChanged(int columnIndex, int newValue) { 420 PickerColumn column = mColumns.get(columnIndex); 421 if (column.getCurrentValue() != newValue) { 422 column.setCurrentValue(newValue); 423 notifyValueChanged(columnIndex); 424 } 425 } 426 427 private float getFloat(int resourceId) { 428 TypedValue buffer = new TypedValue(); 429 getContext().getResources().getValue(resourceId, buffer, true); 430 return buffer.getFloat(); 431 } 432 433 static class ViewHolder extends RecyclerView.ViewHolder { 434 final TextView textView; 435 436 ViewHolder(View v, TextView textView) { 437 super(v); 438 this.textView = textView; 439 } 440 } 441 442 class PickerScrollArrayAdapter extends RecyclerView.Adapter<ViewHolder> { 443 444 private final int mResource; 445 private final int mColIndex; 446 private final int mTextViewResourceId; 447 private PickerColumn mData; 448 449 PickerScrollArrayAdapter(Context context, int resource, int textViewResourceId, 450 int colIndex) { 451 mResource = resource; 452 mColIndex = colIndex; 453 mTextViewResourceId = textViewResourceId; 454 mData = mColumns.get(mColIndex); 455 } 456 457 @Override 458 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 459 LayoutInflater inflater = LayoutInflater.from(parent.getContext()); 460 View v = inflater.inflate(mResource, parent, false); 461 TextView textView; 462 if (mTextViewResourceId != 0) { 463 textView = (TextView) v.findViewById(mTextViewResourceId); 464 } else { 465 textView = (TextView) v; 466 } 467 ViewHolder vh = new ViewHolder(v, textView); 468 return vh; 469 } 470 471 @Override 472 public void onBindViewHolder(ViewHolder holder, int position) { 473 if (holder.textView != null && mData != null) { 474 holder.textView.setText(mData.getLabelFor(mData.getMinValue() + position)); 475 } 476 setOrAnimateAlpha(holder.itemView, 477 (mColumnViews.get(mColIndex).getSelectedPosition() == position), 478 mColIndex, false); 479 } 480 481 @Override 482 public void onViewAttachedToWindow(ViewHolder holder) { 483 holder.itemView.setFocusable(isActivated()); 484 } 485 486 @Override 487 public int getItemCount() { 488 return mData == null ? 0 : mData.getCount(); 489 } 490 } 491 492 private final OnChildViewHolderSelectedListener mColumnChangeListener = new 493 OnChildViewHolderSelectedListener() { 494 495 @Override 496 public void onChildViewHolderSelected(RecyclerView parent, 497 RecyclerView.ViewHolder child, 498 int position, int subposition) { 499 PickerScrollArrayAdapter pickerScrollArrayAdapter = 500 (PickerScrollArrayAdapter) parent 501 .getAdapter(); 502 503 int colIndex = mColumnViews.indexOf(parent); 504 updateColumnAlpha(colIndex, true); 505 if (child != null) { 506 int newValue = mColumns.get(colIndex).getMinValue() + position; 507 onColumnValueChanged(colIndex, newValue); 508 } 509 } 510 511 }; 512 513 @Override 514 public boolean dispatchKeyEvent(android.view.KeyEvent event) { 515 if (isActivated()) { 516 final int keyCode = event.getKeyCode(); 517 switch (keyCode) { 518 case KeyEvent.KEYCODE_DPAD_CENTER: 519 case KeyEvent.KEYCODE_ENTER: 520 if (event.getAction() == KeyEvent.ACTION_UP) { 521 performClick(); 522 } 523 break; 524 default: 525 return super.dispatchKeyEvent(event); 526 } 527 return true; 528 } 529 return super.dispatchKeyEvent(event); 530 } 531 532 @Override 533 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 534 int column = getSelectedColumn(); 535 if (column < mColumnViews.size()) { 536 return mColumnViews.get(column).requestFocus(direction, previouslyFocusedRect); 537 } 538 return false; 539 } 540 541 /** 542 * Classes extending {@link Picker} can choose to override this method to 543 * supply the {@link Picker}'s column's single item height in pixels. 544 */ 545 protected int getPickerItemHeightPixels() { 546 return getContext().getResources().getDimensionPixelSize(R.dimen.picker_item_height); 547 } 548 549 private void updateColumnSize() { 550 for (int i = 0; i < getColumnsCount(); i++) { 551 updateColumnSize(mColumnViews.get(i)); 552 } 553 } 554 555 private void updateColumnSize(VerticalGridView columnView) { 556 ViewGroup.LayoutParams lp = columnView.getLayoutParams(); 557 float itemCount = isActivated() ? getActivatedVisibleItemCount() : getVisibleItemCount(); 558 lp.height = (int) (getPickerItemHeightPixels() * itemCount 559 + columnView.getVerticalSpacing() * (itemCount - 1)); 560 columnView.setLayoutParams(lp); 561 } 562 563 private void updateItemFocusable() { 564 final boolean activated = isActivated(); 565 for (int i = 0; i < getColumnsCount(); i++) { 566 VerticalGridView grid = mColumnViews.get(i); 567 for (int j = 0; j < grid.getChildCount(); j++) { 568 View view = grid.getChildAt(j); 569 view.setFocusable(activated); 570 } 571 } 572 } 573 574 /** 575 * Returns number of visible items showing in a column when it's activated. The default value 576 * is 3. 577 * 578 * @return Number of visible items showing in a column when it's activated. 579 */ 580 public float getActivatedVisibleItemCount() { 581 return mVisibleItemsActivated; 582 } 583 584 /** 585 * Changes number of visible items showing in a column when it's activated. The default value 586 * is 3. 587 * 588 * @param visiblePickerItems Number of visible items showing in a column when it's activated. 589 */ 590 public void setActivatedVisibleItemCount(float visiblePickerItems) { 591 if (visiblePickerItems <= 0) { 592 throw new IllegalArgumentException(); 593 } 594 if (mVisibleItemsActivated != visiblePickerItems) { 595 mVisibleItemsActivated = visiblePickerItems; 596 if (isActivated()) { 597 updateColumnSize(); 598 } 599 } 600 } 601 602 /** 603 * Returns number of visible items showing in a column when it's not activated. The default 604 * value is 1. 605 * 606 * @return Number of visible items showing in a column when it's not activated. 607 */ 608 public float getVisibleItemCount() { 609 return 1; 610 } 611 612 /** 613 * Changes number of visible items showing in a column when it's not activated. The default 614 * value is 1. 615 * 616 * @param pickerItems Number of visible items showing in a column when it's not activated. 617 */ 618 public void setVisibleItemCount(float pickerItems) { 619 if (pickerItems <= 0) { 620 throw new IllegalArgumentException(); 621 } 622 if (mVisibleItems != pickerItems) { 623 mVisibleItems = pickerItems; 624 if (!isActivated()) { 625 updateColumnSize(); 626 } 627 } 628 } 629 630 @Override 631 public void setActivated(boolean activated) { 632 if (activated == isActivated()) { 633 super.setActivated(activated); 634 return; 635 } 636 super.setActivated(activated); 637 boolean hadFocus = hasFocus(); 638 int column = getSelectedColumn(); 639 // To avoid temporary focus loss in both the following cases, we set Picker's flag to 640 // FOCUS_BEFORE_DESCENDANTS first, and then back to FOCUS_AFTER_DESCENDANTS once done with 641 // the focus logic. 642 // 1. When changing from activated to deactivated, the Picker should grab the focus 643 // back if it's focusable. However, calling requestFocus on it will transfer the focus down 644 // to its children if it's flag is FOCUS_AFTER_DESCENDANTS. 645 // 2. When changing from deactivated to activated, while setting focusable flags on each 646 // column VerticalGridView, that column will call requestFocus (regardless of which column 647 // is the selected column) since the currently focused view (Picker) has a flag of 648 // FOCUS_AFTER_DESCENDANTS. 649 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS); 650 if (!activated && hadFocus && isFocusable()) { 651 // When picker widget that originally had focus is deactivated and it is focusable, we 652 // should not pass the focus down to the children. The Picker itself will capture focus. 653 requestFocus(); 654 } 655 656 for (int i = 0; i < getColumnsCount(); i++) { 657 mColumnViews.get(i).setFocusable(activated); 658 } 659 660 updateColumnSize(); 661 updateItemFocusable(); 662 if (activated && hadFocus && (column >= 0)) { 663 mColumnViews.get(column).requestFocus(); 664 } 665 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); 666 } 667 668 @Override 669 public void requestChildFocus(View child, View focused) { 670 super.requestChildFocus(child, focused); 671 for (int i = 0; i < mColumnViews.size(); i++) { 672 if (mColumnViews.get(i).hasFocus()) { 673 setSelectedColumn(i); 674 } 675 } 676 } 677 678 /** 679 * Change current selected column. Picker shows multiple items on selected column if Picker has 680 * focus. Picker shows multiple items on all column if Picker has no focus (e.g. a Touchscreen 681 * screen). 682 * 683 * @param columnIndex Index of column to activate. 684 */ 685 public void setSelectedColumn(int columnIndex) { 686 if (mSelectedColumn != columnIndex) { 687 mSelectedColumn = columnIndex; 688 for (int i = 0; i < mColumnViews.size(); i++) { 689 updateColumnAlpha(i, true); 690 } 691 } 692 } 693 694 /** 695 * Get current activated column index. 696 * 697 * @return Current activated column index. 698 */ 699 public int getSelectedColumn() { 700 return mSelectedColumn; 701 } 702 703 } 704