1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.widget; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.icu.util.Calendar; 22 import android.util.AttributeSet; 23 import android.view.LayoutInflater; 24 import android.view.View; 25 import android.view.ViewGroup; 26 import android.view.accessibility.AccessibilityEvent; 27 28 import com.android.internal.R; 29 30 /** 31 * Displays a selectable list of years. 32 */ 33 class YearPickerView extends ListView { 34 private final YearAdapter mAdapter; 35 private final int mViewSize; 36 private final int mChildSize; 37 38 private OnYearSelectedListener mOnYearSelectedListener; 39 YearPickerView(Context context, AttributeSet attrs)40 public YearPickerView(Context context, AttributeSet attrs) { 41 this(context, attrs, R.attr.listViewStyle); 42 } 43 YearPickerView(Context context, AttributeSet attrs, int defStyleAttr)44 public YearPickerView(Context context, AttributeSet attrs, int defStyleAttr) { 45 this(context, attrs, defStyleAttr, 0); 46 } 47 YearPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)48 public YearPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 49 super(context, attrs, defStyleAttr, defStyleRes); 50 51 final LayoutParams frame = new LayoutParams( 52 LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 53 setLayoutParams(frame); 54 55 final Resources res = context.getResources(); 56 mViewSize = res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height); 57 mChildSize = res.getDimensionPixelOffset(R.dimen.datepicker_year_label_height); 58 59 setOnItemClickListener(new OnItemClickListener() { 60 @Override 61 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 62 final int year = mAdapter.getYearForPosition(position); 63 mAdapter.setSelection(year); 64 65 if (mOnYearSelectedListener != null) { 66 mOnYearSelectedListener.onYearChanged(YearPickerView.this, year); 67 } 68 } 69 }); 70 71 mAdapter = new YearAdapter(getContext()); 72 setAdapter(mAdapter); 73 } 74 setOnYearSelectedListener(OnYearSelectedListener listener)75 public void setOnYearSelectedListener(OnYearSelectedListener listener) { 76 mOnYearSelectedListener = listener; 77 } 78 79 /** 80 * Sets the currently selected year. Jumps immediately to the new year. 81 * 82 * @param year the target year 83 */ setYear(final int year)84 public void setYear(final int year) { 85 mAdapter.setSelection(year); 86 87 post(new Runnable() { 88 @Override 89 public void run() { 90 final int position = mAdapter.getPositionForYear(year); 91 if (position >= 0 && position < getCount()) { 92 setSelectionCentered(position); 93 } 94 } 95 }); 96 } 97 setSelectionCentered(int position)98 public void setSelectionCentered(int position) { 99 final int offset = mViewSize / 2 - mChildSize / 2; 100 setSelectionFromTop(position, offset); 101 } 102 setRange(Calendar min, Calendar max)103 public void setRange(Calendar min, Calendar max) { 104 mAdapter.setRange(min, max); 105 } 106 107 private static class YearAdapter extends BaseAdapter { 108 private static final int ITEM_LAYOUT = R.layout.year_label_text_view; 109 private static final int ITEM_TEXT_APPEARANCE = 110 R.style.TextAppearance_Material_DatePicker_List_YearLabel; 111 private static final int ITEM_TEXT_ACTIVATED_APPEARANCE = 112 R.style.TextAppearance_Material_DatePicker_List_YearLabel_Activated; 113 114 private final LayoutInflater mInflater; 115 116 private int mActivatedYear; 117 private int mMinYear; 118 private int mCount; 119 YearAdapter(Context context)120 public YearAdapter(Context context) { 121 mInflater = LayoutInflater.from(context); 122 } 123 setRange(Calendar minDate, Calendar maxDate)124 public void setRange(Calendar minDate, Calendar maxDate) { 125 final int minYear = minDate.get(Calendar.YEAR); 126 final int count = maxDate.get(Calendar.YEAR) - minYear + 1; 127 128 if (mMinYear != minYear || mCount != count) { 129 mMinYear = minYear; 130 mCount = count; 131 notifyDataSetInvalidated(); 132 } 133 } 134 setSelection(int year)135 public boolean setSelection(int year) { 136 if (mActivatedYear != year) { 137 mActivatedYear = year; 138 notifyDataSetChanged(); 139 return true; 140 } 141 return false; 142 } 143 144 @Override getCount()145 public int getCount() { 146 return mCount; 147 } 148 149 @Override getItem(int position)150 public Integer getItem(int position) { 151 return getYearForPosition(position); 152 } 153 154 @Override getItemId(int position)155 public long getItemId(int position) { 156 return getYearForPosition(position); 157 } 158 getPositionForYear(int year)159 public int getPositionForYear(int year) { 160 return year - mMinYear; 161 } 162 getYearForPosition(int position)163 public int getYearForPosition(int position) { 164 return mMinYear + position; 165 } 166 167 @Override hasStableIds()168 public boolean hasStableIds() { 169 return true; 170 } 171 172 @Override getView(int position, View convertView, ViewGroup parent)173 public View getView(int position, View convertView, ViewGroup parent) { 174 final TextView v; 175 final boolean hasNewView = convertView == null; 176 if (hasNewView) { 177 v = (TextView) mInflater.inflate(ITEM_LAYOUT, parent, false); 178 } else { 179 v = (TextView) convertView; 180 } 181 182 final int year = getYearForPosition(position); 183 final boolean activated = mActivatedYear == year; 184 185 if (hasNewView || v.isActivated() != activated) { 186 final int textAppearanceResId; 187 if (activated && ITEM_TEXT_ACTIVATED_APPEARANCE != 0) { 188 textAppearanceResId = ITEM_TEXT_ACTIVATED_APPEARANCE; 189 } else { 190 textAppearanceResId = ITEM_TEXT_APPEARANCE; 191 } 192 v.setTextAppearance(textAppearanceResId); 193 v.setActivated(activated); 194 } 195 196 v.setText(Integer.toString(year)); 197 return v; 198 } 199 200 @Override getItemViewType(int position)201 public int getItemViewType(int position) { 202 return 0; 203 } 204 205 @Override getViewTypeCount()206 public int getViewTypeCount() { 207 return 1; 208 } 209 210 @Override isEmpty()211 public boolean isEmpty() { 212 return false; 213 } 214 215 @Override areAllItemsEnabled()216 public boolean areAllItemsEnabled() { 217 return true; 218 } 219 220 @Override isEnabled(int position)221 public boolean isEnabled(int position) { 222 return true; 223 } 224 } 225 getFirstPositionOffset()226 public int getFirstPositionOffset() { 227 final View firstChild = getChildAt(0); 228 if (firstChild == null) { 229 return 0; 230 } 231 return firstChild.getTop(); 232 } 233 234 /** @hide */ 235 @Override onInitializeAccessibilityEventInternal(AccessibilityEvent event)236 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { 237 super.onInitializeAccessibilityEventInternal(event); 238 239 // There are a bunch of years, so don't bother. 240 if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) { 241 event.setFromIndex(0); 242 event.setToIndex(0); 243 } 244 } 245 246 /** 247 * The callback used to indicate the user changed the year. 248 */ 249 public interface OnYearSelectedListener { 250 /** 251 * Called upon a year change. 252 * 253 * @param view The view associated with this listener. 254 * @param year The year that was set. 255 */ onYearChanged(YearPickerView view, int year)256 void onYearChanged(YearPickerView view, int year); 257 } 258 }