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