YearPickerView.java revision 50eb025c2fea7e364e0be951ce8ba6ca605f901a
10529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch/*
20529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * Copyright (C) 2014 The Android Open Source Project
30529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch *
40529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * Licensed under the Apache License, Version 2.0 (the "License");
5f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * you may not use this file except in compliance with the License.
6f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) * You may obtain a copy of the License at
70529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch *
80529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch *      http://www.apache.org/licenses/LICENSE-2.0
946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) *
10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * Unless required by applicable law or agreed to in writing, software
110529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * distributed under the License is distributed on an "AS IS" BASIS,
120529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * See the License for the specific language governing permissions and
14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) * limitations under the License.
155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) */
161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
170529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochpackage android.widget;
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuimport android.content.Context;
200529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.content.res.Resources;
210529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport android.util.AttributeSet;
22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import android.view.View;
23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import android.view.ViewGroup;
24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import android.view.accessibility.AccessibilityEvent;
25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
260529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport java.util.Calendar;
27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
280529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport com.android.internal.R;
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/**
311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * Displays a selectable list of years.
320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch */
33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)class YearPickerView extends ListView implements AdapterView.OnItemClickListener,
34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        OnDateChangedListener {
35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    private final Calendar mMinDate = Calendar.getInstance();
36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    private final Calendar mMaxDate = Calendar.getInstance();
37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    private final YearAdapter mAdapter;
39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    private final int mViewSize;
400529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    private final int mChildSize;
41f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    private DatePickerController mController;
435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
440529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    private int mSelectedPosition = -1;
451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    private int mYearSelectedCircleColor;
46f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
47f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    public YearPickerView(Context context) {
48116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        this(context, null);
491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
51f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    public YearPickerView(Context context, AttributeSet attrs) {
520529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        this(context, attrs, R.attr.listViewStyle);
53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
54f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    public YearPickerView(Context context, AttributeSet attrs, int defStyleAttr) {
560529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        this(context, attrs, defStyleAttr, 0);
57f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
58f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
5946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    public YearPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
600529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        super(context, attrs, defStyleAttr, defStyleRes);
611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        final LayoutParams frame = new LayoutParams(
631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        setLayoutParams(frame);
65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        final Resources res = context.getResources();
670529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        mViewSize = res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height);
68116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        mChildSize = res.getDimensionPixelOffset(R.dimen.datepicker_year_label_height);
691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        setVerticalFadingEdgeEnabled(true);
7146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        setFadingEdgeLength(mChildSize / 3);
72f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
7346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        final int paddingTop = res.getDimensionPixelSize(
74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                R.dimen.datepicker_year_picker_padding_top);
75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        setPadding(0, paddingTop, 0, 0);
76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        setOnItemClickListener(this);
78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        setDividerHeight(0);
79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        mAdapter = new YearAdapter(getContext(), R.layout.year_label_text_view);
811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        setAdapter(mAdapter);
821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    public void setRange(Calendar min, Calendar max) {
851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        mMinDate.setTimeInMillis(min.getTimeInMillis());
861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        mMaxDate.setTimeInMillis(max.getTimeInMillis());
871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        updateAdapterData();
891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
91f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    public void init(DatePickerController controller) {
92f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        mController = controller;
931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        mController.registerOnDateChangedListener(this);
941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        updateAdapterData();
96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        onDateChanged();
98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    public void setYearSelectedCircleColor(int color) {
1010529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        if (color != mYearSelectedCircleColor) {
1020529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            mYearSelectedCircleColor = color;
1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        }
1041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        requestLayout();
105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
1061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    public int getYearSelectedCircleColor()  {
108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        return mYearSelectedCircleColor;
109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
1101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    private void updateAdapterData() {
112f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        mAdapter.clear();
1135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        final int maxYear = mMaxDate.get(Calendar.YEAR);
1155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        for (int year = mMinDate.get(Calendar.YEAR); year <= maxYear; year++) {
1161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            mAdapter.add(year);
1175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        }
1181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
1191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    @Override
1211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        mController.tryVibrate();
1231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        if (position != mSelectedPosition) {
1241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            mSelectedPosition = position;
1251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            mAdapter.notifyDataSetChanged();
1261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        }
1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        mController.onYearSelected(mAdapter.getItem(position));
128116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    }
1291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    void setItemTextAppearance(int resId) {
131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        mAdapter.setItemTextAppearance(resId);
1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
13346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
1341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    private class YearAdapter extends ArrayAdapter<Integer> {
1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        int mItemTextAppearanceResId;
1366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        public YearAdapter(Context context, int resource) {
1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            super(context, resource);
1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        }
1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        @Override
1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        public View getView(int position, View convertView, ViewGroup parent) {
1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            TextViewWithCircularIndicator v = (TextViewWithCircularIndicator)
1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                    super.getView(position, convertView, parent);
1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            v.setTextAppearance(getContext(), mItemTextAppearanceResId);
1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            v.requestLayout();
1471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            int year = getItem(position);
1481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            boolean selected = mController.getSelectedDay().get(Calendar.YEAR) == year;
1495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu            v.setDrawIndicator(selected);
150116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            if (selected) {
1516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                v.setCircleColor(mYearSelectedCircleColor);
152116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            }
1536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            return v;
1545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        }
1556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
156f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        public void setItemTextAppearance(int resId) {
1570529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            mItemTextAppearanceResId = resId;
1586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        }
1591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
1600529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch
1616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    public void postSetSelectionCentered(final int position) {
1626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        postSetSelectionFromTop(position, mViewSize / 2 - mChildSize / 2);
163f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
164f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    public void postSetSelectionFromTop(final int position, final int offset) {
1666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        post(new Runnable() {
1676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            @Override
1691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            public void run() {
1701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                setSelectionFromTop(position, offset);
1716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                requestLayout();
172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)            }
173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        });
1746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
175116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
176116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    public int getFirstPositionOffset() {
1775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        final View firstChild = getChildAt(0);
1786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        if (firstChild == null) {
1796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            return 0;
1805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        }
1816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        return firstChild.getTop();
1820529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    }
1835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    @Override
1850de6073388f4e2780db8536178b129cd8f6ab386Torne (Richard Coles)    public void onDateChanged() {
1865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        updateAdapterData();
1875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        mAdapter.notifyDataSetChanged();
1881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        postSetSelectionCentered(
1891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                mController.getSelectedDay().get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR));
1901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
1911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    @Override
1931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        super.onInitializeAccessibilityEvent(event);
1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
196f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
1970529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            event.setFromIndex(0);
19846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)            event.setToIndex(0);
19946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        }
20046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    }
20146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}