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