├── .settings ├── org.eclipse.core.resources.prefs └── org.eclipse.jdt.core.prefs ├── ic_launcher-web.png ├── image └── finalimage.png ├── libs └── android-support-v4.jar ├── res ├── drawable-hdpi │ └── ic_launcher.png ├── drawable-mdpi │ └── ic_launcher.png ├── drawable-xhdpi │ └── ic_launcher.png ├── drawable-xxhdpi │ └── ic_launcher.png ├── anim │ ├── slide_out_to_bottom.xml │ └── slide_in_from_bottom.xml ├── drawable │ ├── wheel_bg_day.xml │ ├── wheel_fg_day.xml │ └── wheel_val.xml ├── values │ ├── strings.xml │ └── styles.xml ├── values-v11 │ └── styles.xml ├── values-v14 │ └── styles.xml └── layout │ ├── item_wheel_view.xml │ ├── item_wheel_view_align_left.xml │ ├── item_wheel_view_align_right.xml │ ├── layout_page_popup_view.xml │ └── activity_main.xml ├── README.md ├── .classpath ├── src └── com │ ├── wanmei │ └── dospy │ │ └── view │ │ ├── wheel │ │ ├── IWheelScrollListener.java │ │ ├── IWheelClickedListener.java │ │ ├── YearOrMonthAdapter.java │ │ ├── IWheelChangedListener.java │ │ ├── ArrayWheelAdapter.java │ │ ├── AbstractWheelAdapter.java │ │ ├── IWheelViewAdapter.java │ │ ├── ItemsRange.java │ │ ├── WheelRecycle.java │ │ ├── AbstractWheelTextAdapter.java │ │ ├── WheelScroller.java │ │ └── WheelView.java │ │ ├── CustomDialog.java │ │ └── PageHelper.java │ └── example │ └── android_year_month_wheel │ └── MainActivity.java ├── project.properties ├── proguard-project.txt ├── .project └── AndroidManifest.xml /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 3 | -------------------------------------------------------------------------------- /ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/year_month_wheel_dialog/HEAD/ic_launcher-web.png -------------------------------------------------------------------------------- /image/finalimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/year_month_wheel_dialog/HEAD/image/finalimage.png -------------------------------------------------------------------------------- /libs/android-support-v4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/year_month_wheel_dialog/HEAD/libs/android-support-v4.jar -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/year_month_wheel_dialog/HEAD/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/year_month_wheel_dialog/HEAD/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/year_month_wheel_dialog/HEAD/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/year_month_wheel_dialog/HEAD/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 3 | org.eclipse.jdt.core.compiler.compliance=1.6 4 | org.eclipse.jdt.core.compiler.source=1.6 5 | -------------------------------------------------------------------------------- /res/anim/slide_out_to_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # year_month_wheel_dialog 2 | Year Month Wheel Dialog is a view to choose year and month by double roller dialog (能够选择年月的双滚轮dialog) 3 | 效果图: 4 | ![Alt text](https://github.com/xuningjack/year_month_wheel_dialog/raw/master/image/finalimage.png) 5 | -------------------------------------------------------------------------------- /res/drawable/wheel_bg_day.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Android_year_month_wheel 5 | Hello world! 6 | %d年 7 | %d月 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/wheel/IWheelScrollListener.java: -------------------------------------------------------------------------------- 1 | package com.wanmei.dospy.view.wheel; 2 | 3 | /** 4 | * Wheel scrolled listener interface. 5 | */ 6 | public interface IWheelScrollListener { 7 | /** 8 | * Callback method to be invoked when scrolling started. 9 | * @param wheel the wheel view whose state has changed. 10 | */ 11 | void onScrollingStarted(WheelView wheel); 12 | 13 | /** 14 | * Callback method to be invoked when scrolling ended. 15 | * @param wheel the wheel view whose state has changed. 16 | */ 17 | void onScrollingFinished(WheelView wheel); 18 | } 19 | -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/wheel/IWheelClickedListener.java: -------------------------------------------------------------------------------- 1 | package com.wanmei.dospy.view.wheel; 2 | 3 | /** 4 | * Wheel clicked listener interface. 5 | *

The onItemClicked() method is called whenever a wheel item is clicked 6 | *

  • New Wheel position is set 7 | *
  • Wheel view is scrolled 8 | */ 9 | public interface IWheelClickedListener { 10 | /** 11 | * Callback method to be invoked when current item clicked 12 | * @param wheel the wheel view 13 | * @param itemIndex the index of clicked item 14 | */ 15 | void onItemClicked(WheelView wheel, int itemIndex); 16 | } 17 | -------------------------------------------------------------------------------- /res/layout/item_wheel_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 17 | 18 | -------------------------------------------------------------------------------- /project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-16 15 | -------------------------------------------------------------------------------- /res/layout/item_wheel_view_align_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 18 | 19 | -------------------------------------------------------------------------------- /res/drawable/wheel_fg_day.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /res/layout/item_wheel_view_align_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 18 | 19 | -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/wheel/YearOrMonthAdapter.java: -------------------------------------------------------------------------------- 1 | package com.wanmei.dospy.view.wheel; 2 | 3 | import java.util.List; 4 | 5 | import android.content.Context; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | /** 10 | * 显示年月的双滚轮 11 | */ 12 | public class YearOrMonthAdapter extends ArrayWheelAdapter { 13 | 14 | public YearOrMonthAdapter(Context context, List items, int itemResource) { 15 | super(context, items, itemResource); 16 | } 17 | 18 | @Override 19 | public View getItem(int index, View convertView, ViewGroup parent) { 20 | return super.getItem(index, convertView, parent); 21 | } 22 | } -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/wheel/IWheelChangedListener.java: -------------------------------------------------------------------------------- 1 | package com.wanmei.dospy.view.wheel; 2 | 3 | /** 4 | * Wheel changed listener interface. 5 | *

    The onChanged() method is called whenever current wheel positions is changed: 6 | *

  • New Wheel position is set 7 | *
  • Wheel view is scrolled 8 | */ 9 | public interface IWheelChangedListener { 10 | /** 11 | * Callback method to be invoked when current item changed 12 | * @param wheel the wheel view whose state has changed 13 | * @param oldValue the old value of current item 14 | * @param newValue the new value of current item 15 | */ 16 | void onChanged(WheelView wheel, int oldValue, int newValue); 17 | } 18 | -------------------------------------------------------------------------------- /proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /res/anim/slide_in_from_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 22 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | Android_year_month_wheel 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /res/layout/layout_page_popup_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 24 | 25 | -------------------------------------------------------------------------------- /res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 20 | 21 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /res/drawable/wheel_val.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 22 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/CustomDialog.java: -------------------------------------------------------------------------------- 1 | package com.wanmei.dospy.view; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup.LayoutParams; 9 | import com.example.android_year_month_wheel.R; 10 | 11 | public class CustomDialog extends Dialog { 12 | 13 | private Context mContext; 14 | 15 | public CustomDialog(Context context, int theme) { 16 | super(context, theme); 17 | mContext = context; 18 | } 19 | 20 | public CustomDialog(Context context) { 21 | super(context); 22 | mContext = context; 23 | } 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | } 29 | 30 | public CustomDialog createPopupWindow() { 31 | LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 32 | final CustomDialog dialog = new CustomDialog(mContext, R.style.DialogPopupWindow); 33 | View layout = inflater.inflate(R.layout.layout_page_popup_view, null); 34 | dialog.addContentView(layout, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); 35 | return dialog; 36 | } 37 | } -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/wheel/ArrayWheelAdapter.java: -------------------------------------------------------------------------------- 1 | package com.wanmei.dospy.view.wheel; 2 | 3 | import java.util.List; 4 | import android.content.Context; 5 | import com.example.android_year_month_wheel.R; 6 | 7 | /** 8 | * The simple Array wheel adapter 9 | * @param the element type 10 | */ 11 | public class ArrayWheelAdapter extends AbstractWheelTextAdapter { 12 | 13 | // items 14 | private List items; 15 | 16 | /** 17 | * 构造方法 18 | * @param context 19 | * @param items 布局文件,如R.layout.item_wheel_view 20 | * @param itemResource 21 | */ 22 | public ArrayWheelAdapter(Context context, List items, int itemResource) { 23 | super(context, itemResource, R.id.text); 24 | this.items = items; 25 | } 26 | 27 | /** 28 | * Constructor 29 | * @param context the current context 30 | * @param items the items 31 | */ 32 | public ArrayWheelAdapter(Context context, List items) { 33 | super(context, R.layout.item_wheel_view, R.id.name); 34 | this.items = items; 35 | } 36 | 37 | 38 | @Override 39 | public CharSequence getItemText(int index) { 40 | if (index >= 0 && index < items.size()) { 41 | T item = items.get(index); 42 | if (item instanceof CharSequence) { 43 | return (CharSequence) item; 44 | } 45 | return item.toString(); 46 | } 47 | return null; 48 | } 49 | 50 | @Override 51 | public int getItemsCount() { 52 | return items.size(); 53 | } 54 | } -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/wheel/AbstractWheelAdapter.java: -------------------------------------------------------------------------------- 1 | package com.wanmei.dospy.view.wheel; 2 | 3 | import android.database.DataSetObserver; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | 10 | public abstract class AbstractWheelAdapter implements IWheelViewAdapter { 11 | private List mDatasetObservers; 12 | 13 | @Override 14 | public View getEmptyItem(View convertView, ViewGroup parent) { 15 | return null; 16 | } 17 | 18 | @Override 19 | public void registerDataSetObserver(DataSetObserver observer) { 20 | if (mDatasetObservers == null) { 21 | mDatasetObservers = new LinkedList(); 22 | } 23 | mDatasetObservers.add(observer); 24 | } 25 | 26 | @Override 27 | public void unregisterDataSetObserver(DataSetObserver observer) { 28 | if (mDatasetObservers != null) { 29 | mDatasetObservers.remove(observer); 30 | } 31 | } 32 | 33 | /** 34 | * Notifies observers about data changing 35 | */ 36 | protected void notifyDataChangedEvent() { 37 | if (mDatasetObservers != null) { 38 | for (DataSetObserver observer : mDatasetObservers) { 39 | observer.onChanged(); 40 | } 41 | } 42 | } 43 | 44 | /** 45 | * Notifies observers about invalidating data 46 | */ 47 | protected void notifyDataInvalidatedEvent() { 48 | if (mDatasetObservers != null) { 49 | for (DataSetObserver observer : mDatasetObservers) { 50 | observer.onInvalidated(); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 27 | 28 | 32 | 33 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/wheel/IWheelViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.wanmei.dospy.view.wheel; 2 | 3 | import android.database.DataSetObserver; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.TextView; 7 | 8 | /** 9 | * Wheel items adapter interface 10 | */ 11 | public interface IWheelViewAdapter { 12 | /** 13 | * Gets items count 14 | * @return the count of wheel items 15 | */ 16 | public int getItemsCount(); 17 | 18 | /** 19 | * Get a View that displays the data at the specified position in the data set 20 | * 21 | * @param index the item index 22 | * @param convertView the old view to reuse if possible 23 | * @param parent the parent that this view will eventually be attached to 24 | * @return the wheel item View 25 | */ 26 | public View getItem(int index, View convertView, ViewGroup parent); 27 | 28 | /** 29 | * Get a View that displays an empty wheel item placed before the first or after 30 | * the last wheel item. 31 | * 32 | * @param convertView the old view to reuse if possible 33 | * @param parent the parent that this view will eventually be attached to 34 | * @return the empty item View 35 | */ 36 | public View getEmptyItem(View convertView, ViewGroup parent); 37 | 38 | /** 39 | * Register an observer that is called when changes happen to the data used by this adapter. 40 | * @param observer the observer to be registered 41 | */ 42 | void registerDataSetObserver(DataSetObserver observer); 43 | 44 | /** 45 | * Unregister an observer that has previously been registered 46 | * @param observer the observer to be unregistered 47 | */ 48 | void unregisterDataSetObserver(DataSetObserver observer); 49 | 50 | TextView getTextView(View view, int textResource); 51 | 52 | int getTextViewItemResourceId(); 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/wheel/ItemsRange.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Android Wheel Control. 3 | * https://code.google.com/p/android-wheel/ 4 | * 5 | * Copyright 2011 Yuri Kanivets 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | package com.wanmei.dospy.view.wheel; 21 | 22 | /** 23 | * Range for visible items. 24 | */ 25 | public class ItemsRange { 26 | // First item number 27 | private int first; 28 | 29 | // Items count 30 | private int count; 31 | 32 | /** 33 | * Default constructor. Creates an empty range 34 | */ 35 | public ItemsRange() { 36 | this(0, 0); 37 | } 38 | 39 | /** 40 | * Constructor 41 | * @param first the number of first item 42 | * @param count the count of items 43 | */ 44 | public ItemsRange(int first, int count) { 45 | this.first = first; 46 | this.count = count; 47 | } 48 | 49 | /** 50 | * Gets number of first item 51 | * @return the number of the first item 52 | */ 53 | public int getFirst() { 54 | return first; 55 | } 56 | 57 | /** 58 | * Gets number of last item 59 | * @return the number of last item 60 | */ 61 | public int getLast() { 62 | return getFirst() + getCount() - 1; 63 | } 64 | 65 | /** 66 | * Get items count 67 | * @return the count of items 68 | */ 69 | public int getCount() { 70 | return count; 71 | } 72 | 73 | /** 74 | * Tests whether item is contained by range 75 | * @param index the item number 76 | * @return true if item is contained 77 | */ 78 | public boolean contains(int index) { 79 | return index >= getFirst() && index <= getLast(); 80 | } 81 | } -------------------------------------------------------------------------------- /src/com/example/android_year_month_wheel/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.android_year_month_wheel; 2 | 3 | import java.util.Calendar; 4 | 5 | import android.app.Activity; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | import android.widget.TextView; 9 | 10 | import com.wanmei.dospy.view.PageHelper; 11 | 12 | public class MainActivity extends Activity { 13 | 14 | private PageHelper mPageHelper; 15 | private TextView mFromDate, mToDate; 16 | private int mMaxYear, mSelectYear, mSelectMonth; 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_main); 22 | 23 | Calendar calendar = Calendar.getInstance(); 24 | mMaxYear = calendar.get(Calendar.YEAR); 25 | mFromDate = (TextView) findViewById(R.id.from_date); 26 | mToDate = (TextView)findViewById(R.id.to_date); 27 | mPageHelper = new PageHelper(MainActivity.this, new View.OnClickListener() { 28 | 29 | @Override 30 | public void onClick(View arg0) { 31 | // TODO Auto-generated method stub 32 | 33 | } 34 | }); 35 | 36 | final int FROM_YEAR = 1990; 37 | mPageHelper.setYearArrange(FROM_YEAR, mMaxYear); 38 | // mPageHelper.setMaxPageNum(mMaxYear); //设置显示的最大页数 39 | mPageHelper.selectYearAndMonth(mSelectYear, mSelectMonth); 40 | mFromDate.setOnClickListener(new View.OnClickListener() { 41 | @Override 42 | public void onClick(View v) { 43 | mPageHelper.showPopup(mSelectYear, mSelectMonth); // 设置当前显示页 44 | } 45 | }); 46 | 47 | final int FROM_MONTH = 1; 48 | mPageHelper.setMonthArrange(1, 12); 49 | 50 | mPageHelper.setListener(new PageHelper.IPageSelectedListener() { 51 | @Override 52 | public void onPageSelected(int page) { 53 | 54 | } 55 | 56 | @Override 57 | public void onYearMonthSelected(int year, int month) { 58 | mSelectYear = year; 59 | mSelectMonth = month; 60 | String yearStr = getString(R.string.year_prompt, year + FROM_YEAR); 61 | String monthStr = getString(R.string.month_prompt, month + FROM_MONTH); 62 | mFromDate.setText(yearStr + "\t" + monthStr); 63 | } 64 | }); 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/wheel/WheelRecycle.java: -------------------------------------------------------------------------------- 1 | package com.wanmei.dospy.view.wheel; 2 | 3 | import android.view.View; 4 | import android.widget.LinearLayout; 5 | 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | /** 10 | * Recycle stores mWheelView mItems to reuse. 11 | */ 12 | public final class WheelRecycle { 13 | private WheelView mWheelView; 14 | // Cached mItems 15 | private List mItems; 16 | // Cached empty mItems 17 | private List mEmptyItems; 18 | 19 | public WheelRecycle(WheelView wheel) { 20 | mWheelView = wheel; 21 | } 22 | 23 | public void clearAll() { 24 | if (mItems != null) { 25 | mItems.clear(); 26 | } 27 | if (mEmptyItems != null) { 28 | mEmptyItems.clear(); 29 | } 30 | } 31 | 32 | 33 | /** 34 | * Recycles mItems from specified layout. 35 | * There are saved only mItems not included to specified range. 36 | * All the cached mItems are removed from original layout. 37 | * 38 | * @param layout the layout containing mItems to be cached 39 | * @param firstItem the number of first item in layout 40 | * @param range the range of current mWheelView mItems 41 | * @return the new value of first item number 42 | */ 43 | public int recycleItems(LinearLayout layout, int firstItem, ItemsRange range) { 44 | int index = firstItem; 45 | for (int i = 0; i < layout.getChildCount(); ) { 46 | if (!range.contains(index)) { 47 | recycleView(layout.getChildAt(i), index); 48 | layout.removeViewAt(i); 49 | if (i == 0) { // first item 50 | firstItem++; 51 | } 52 | } else { 53 | i++; // go to next item 54 | } 55 | index++; 56 | } 57 | return firstItem; 58 | } 59 | 60 | /** 61 | * Gets item view 62 | * 63 | * @return the cached view 64 | */ 65 | public View getItem() { 66 | return getCachedView(mItems); 67 | } 68 | 69 | /** 70 | * Gets empty item view 71 | * 72 | * @return the cached empty view 73 | */ 74 | public View getEmptyItem() { 75 | return getCachedView(mEmptyItems); 76 | } 77 | 78 | 79 | /** 80 | * Adds view to specified cache. Creates a cache list if it is null. 81 | * 82 | * @param view the view to be cached 83 | * @param cache the cache list 84 | * @return the cache list 85 | */ 86 | private List addView(View view, List cache) { 87 | if (cache == null) { 88 | cache = new LinkedList(); 89 | } 90 | 91 | cache.add(view); 92 | return cache; 93 | } 94 | 95 | /** 96 | * Adds view to cache. Determines view type (item view or empty one) by index. 97 | * 98 | * @param view the view to be cached 99 | * @param index the index of view 100 | */ 101 | private void recycleView(View view, int index) { 102 | int count = mWheelView.getViewAdapter().getItemsCount(); 103 | 104 | if ((index < 0 || index >= count) && !mWheelView.isCyclic()) { 105 | // empty view 106 | mEmptyItems = addView(view, mEmptyItems); 107 | } else { 108 | while (index < 0) { 109 | index = count + index; 110 | } 111 | index %= count; 112 | mItems = addView(view, mItems); 113 | } 114 | } 115 | 116 | /** 117 | * Gets view from specified cache. 118 | * 119 | * @param cache the cache 120 | * @return the first view from cache. 121 | */ 122 | private View getCachedView(List cache) { 123 | if (cache != null && cache.size() > 0) { 124 | View view = cache.get(0); 125 | cache.remove(0); 126 | return view; 127 | } 128 | return null; 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/wheel/AbstractWheelTextAdapter.java: -------------------------------------------------------------------------------- 1 | package com.wanmei.dospy.view.wheel; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.view.Gravity; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | 11 | public abstract class AbstractWheelTextAdapter extends AbstractWheelAdapter { 12 | /** 13 | * Text view resource. Used as a default view for adapter. 14 | */ 15 | public static final int TEXT_VIEW_ITEM_RESOURCE = -1; 16 | /** 17 | * No resource constant. 18 | */ 19 | protected static final int NO_RESOURCE = 0; 20 | /** 21 | * Default text color 22 | */ 23 | public static final int DEFAULT_TEXT_COLOR = 0xFF101010; 24 | /** 25 | * Default text size 26 | */ 27 | public static final int DEFAULT_TEXT_SIZE = 24; 28 | 29 | protected Context mContext; 30 | protected int mItemResourceId; 31 | protected int mItemTextResourceId; 32 | protected LayoutInflater mInflater; 33 | protected int mEmptyItemResourceId; 34 | 35 | protected AbstractWheelTextAdapter(Context context) { 36 | this(context, TEXT_VIEW_ITEM_RESOURCE); 37 | } 38 | 39 | protected AbstractWheelTextAdapter(Context context, int itemResource) { 40 | this(context, itemResource, NO_RESOURCE); 41 | } 42 | 43 | protected AbstractWheelTextAdapter(Context context, int itemResource, int itemTextResource) { 44 | mContext = context; 45 | mItemResourceId = itemResource; 46 | mItemTextResourceId = itemTextResource; 47 | 48 | mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 49 | } 50 | 51 | @Override 52 | public View getItem(int index, View convertView, ViewGroup parent) { 53 | if (index >= 0 && index < getItemsCount()) { 54 | if (convertView == null) { 55 | convertView = getView(mItemResourceId, parent); 56 | } 57 | TextView textView = getTextView(convertView, mItemTextResourceId); 58 | if (textView != null) { 59 | CharSequence text = getItemText(index); 60 | textView.setText(text == null ? "" : text); 61 | 62 | if (mItemResourceId == TEXT_VIEW_ITEM_RESOURCE) { 63 | configureTextView(textView); 64 | } 65 | } 66 | return convertView; 67 | } 68 | return null; 69 | } 70 | 71 | @Override 72 | public View getEmptyItem(View convertView, ViewGroup parent) { 73 | if (convertView == null) { 74 | convertView = getView(mEmptyItemResourceId, parent); 75 | } 76 | if (mEmptyItemResourceId == TEXT_VIEW_ITEM_RESOURCE && convertView instanceof TextView) { 77 | configureTextView((TextView) convertView); 78 | } 79 | 80 | return convertView; 81 | } 82 | 83 | /** 84 | * Loads view from resources 85 | * 86 | * @param resource the resource Id 87 | * @return the loaded view or null if resource is not set 88 | */ 89 | private View getView(int resource, ViewGroup parent) { 90 | switch (resource) { 91 | case NO_RESOURCE: 92 | return null; 93 | case TEXT_VIEW_ITEM_RESOURCE: 94 | return new TextView(mContext); 95 | default: 96 | return mInflater.inflate(resource, parent, false); 97 | } 98 | } 99 | 100 | /** 101 | * Loads a text view from view 102 | * 103 | * @param view the text view or layout containing it 104 | * @param textResource the text resource Id in layout 105 | * @return the loaded text view 106 | */ 107 | public TextView getTextView(View view, int textResource) { 108 | TextView text = null; 109 | try { 110 | if (textResource == NO_RESOURCE && view instanceof TextView) { 111 | text = (TextView) view; 112 | } else if (textResource != NO_RESOURCE) { 113 | text = (TextView) view.findViewById(textResource); 114 | } 115 | } catch (ClassCastException e) { 116 | throw new IllegalStateException( 117 | "AbstractWheelAdapter requires the resource ID to be a TextView", e); 118 | } 119 | return text; 120 | } 121 | 122 | /** 123 | * Returns text for specified item 124 | * 125 | * @param index the item index 126 | * @return the text of specified items 127 | */ 128 | protected abstract CharSequence getItemText(int index); 129 | 130 | /** 131 | * Configures text view. Is called for the TEXT_VIEW_ITEM_RESOURCE views. 132 | * 133 | * @param view the text view to be configured 134 | */ 135 | protected void configureTextView(TextView view) { 136 | //view.setTextColor(DEFAULT_TEXT_COLOR); 137 | view.setGravity(Gravity.CENTER); 138 | view.setTextSize(DEFAULT_TEXT_SIZE); 139 | view.setLines(1); 140 | // if(AppConfiguration.getInstance(mContext).isNightMode()){ 141 | // view.setTextColor(mContext.getResources().getColor(R.color.white)); 142 | // }else{ 143 | view.setTextColor(mContext.getResources().getColor(android.R.color.black)); 144 | // } 145 | view.setTypeface(Typeface.SANS_SERIF, Typeface.BOLD); 146 | } 147 | 148 | @Override 149 | public int getTextViewItemResourceId() { 150 | return mItemTextResourceId; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/wheel/WheelScroller.java: -------------------------------------------------------------------------------- 1 | package com.wanmei.dospy.view.wheel; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.os.Message; 6 | import android.view.GestureDetector; 7 | import android.view.GestureDetector.SimpleOnGestureListener; 8 | import android.view.MotionEvent; 9 | import android.view.animation.Interpolator; 10 | import android.widget.Scroller; 11 | 12 | 13 | public final class WheelScroller { 14 | private final int MESSAGE_SCROLL = 0; 15 | private final int MESSAGE_JUSTIFY = 1; 16 | 17 | /** 18 | * Scrolling duration 19 | */ 20 | private static final int SCROLLING_DURATION = 400; 21 | 22 | /** 23 | * Minimum delta for scrolling 24 | */ 25 | public static final int MIN_DELTA_FOR_SCROLLING = 1; 26 | 27 | /** 28 | * Scrolling mListener interface 29 | */ 30 | public interface IScrollingListener { 31 | /** 32 | * Starting callback called when scrolling is started 33 | */ 34 | void onStarted(); 35 | 36 | /** 37 | * Scrolling callback called when scrolling is performed. 38 | * 39 | * @param distance the distance to scroll 40 | */ 41 | void onScroll(int distance); 42 | 43 | /** 44 | * Finishing callback called after justifying 45 | */ 46 | void onFinished(); 47 | 48 | /** 49 | * Justifying callback called to justify a view when scrolling is ended 50 | */ 51 | void onJustify(); 52 | } 53 | 54 | private GestureDetector mGestureDetector; 55 | private Scroller mScroller; 56 | private IScrollingListener mListener; 57 | private Context mContext; 58 | 59 | private int mLastScrollY; 60 | private float mLastTouchedY; 61 | private boolean mIsScrollingPerformed; 62 | 63 | private SimpleOnGestureListener mGestureListener = new SimpleOnGestureListener() { 64 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 65 | // Do scrolling in onTouchEvent() since onScroll() are not call immediately 66 | // when user touch and move the wheel 67 | return true; 68 | } 69 | 70 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 71 | mLastScrollY = 0; 72 | final int maxY = 0x7FFFFFFF; 73 | final int minY = -maxY; 74 | mScroller.fling(0, mLastScrollY, 0, (int) -velocityY, 0, 0, minY, maxY); 75 | setNextMessage(MESSAGE_SCROLL); 76 | return true; 77 | } 78 | }; 79 | 80 | private Handler mAnimationHandler = new Handler() { 81 | 82 | public void handleMessage(Message msg) { 83 | mScroller.computeScrollOffset(); 84 | int currY = mScroller.getCurrY(); 85 | int delta = mLastScrollY - currY; 86 | mLastScrollY = currY; 87 | if (delta != 0) { 88 | mListener.onScroll(delta); 89 | } 90 | 91 | // scrolling is not finished when it comes to final Y 92 | // so, finish it manually 93 | if (Math.abs(currY - mScroller.getFinalY()) < MIN_DELTA_FOR_SCROLLING) { 94 | currY = mScroller.getFinalY(); 95 | mScroller.forceFinished(true); 96 | } 97 | if (!mScroller.isFinished()) { 98 | mAnimationHandler.sendEmptyMessage(msg.what); 99 | } else if (msg.what == MESSAGE_SCROLL) { 100 | justify(); 101 | } else { 102 | finishScrolling(); 103 | } 104 | } 105 | }; 106 | 107 | public WheelScroller(Context context, IScrollingListener listener) { 108 | mGestureDetector = new GestureDetector(context, mGestureListener); 109 | mGestureDetector.setIsLongpressEnabled(false); 110 | 111 | mScroller = new Scroller(context); 112 | 113 | mListener = listener; 114 | mContext = context; 115 | } 116 | 117 | /** 118 | * Handles Touch event 119 | * 120 | * @param event the motion event 121 | * @return 122 | */ 123 | public boolean onTouchEvent(MotionEvent event) { 124 | switch (event.getAction()) { 125 | case MotionEvent.ACTION_DOWN: 126 | mLastTouchedY = event.getY(); 127 | mScroller.forceFinished(true); 128 | clearMessages(); 129 | break; 130 | case MotionEvent.ACTION_MOVE: 131 | // perform scrolling 132 | int distanceY = (int) (event.getY() - mLastTouchedY); 133 | //use phone`s density to controll the gesture 134 | if (Math.abs(distanceY) >= 10) { //TODO DospyApplication.getInstance().getmDensity() * 2 135 | startScrolling(); 136 | mListener.onScroll(distanceY); 137 | mLastTouchedY = event.getY(); 138 | } 139 | break; 140 | } 141 | if (!mGestureDetector.onTouchEvent(event) && event.getAction() == MotionEvent.ACTION_UP) { 142 | justify(); 143 | } 144 | return true; 145 | } 146 | 147 | /** 148 | * Set next message to queue. Clears queue before. 149 | * 150 | * @param message the message to set 151 | */ 152 | private void setNextMessage(int message) { 153 | clearMessages(); 154 | mAnimationHandler.sendEmptyMessage(message); 155 | } 156 | 157 | /** 158 | * Clears messages from queue 159 | */ 160 | private void clearMessages() { 161 | mAnimationHandler.removeMessages(MESSAGE_SCROLL); 162 | mAnimationHandler.removeMessages(MESSAGE_JUSTIFY); 163 | } 164 | 165 | /** 166 | * Justifies wheel 167 | */ 168 | private void justify() { 169 | mListener.onJustify(); 170 | setNextMessage(MESSAGE_JUSTIFY); 171 | } 172 | 173 | /** 174 | * Finishes scrolling 175 | */ 176 | private void finishScrolling() { 177 | if (mIsScrollingPerformed) { 178 | mListener.onFinished(); 179 | mIsScrollingPerformed = false; 180 | } 181 | } 182 | 183 | /** 184 | * Starts scrolling 185 | */ 186 | private void startScrolling() { 187 | if (!mIsScrollingPerformed) { 188 | mIsScrollingPerformed = true; 189 | mListener.onStarted(); 190 | } 191 | } 192 | 193 | /** 194 | * Scroll the wheel 195 | * 196 | * @param distance the scrolling distance 197 | * @param time the scrolling duration 198 | */ 199 | public void scroll(int distance, int time) { 200 | mScroller.forceFinished(true); 201 | mLastScrollY = 0; 202 | mScroller.startScroll(0, 0, 0, distance, time != 0 ? time : SCROLLING_DURATION); 203 | setNextMessage(MESSAGE_SCROLL); 204 | startScrolling(); 205 | } 206 | 207 | /** 208 | * Stops scrolling 209 | */ 210 | public void stopScrolling() { 211 | mScroller.forceFinished(true); 212 | } 213 | 214 | /** 215 | * Set the the specified scrolling interpolator 216 | * 217 | * @param interpolator the interpolator 218 | */ 219 | public void setInterpolator(Interpolator interpolator) { 220 | mScroller.forceFinished(true); 221 | mScroller = new Scroller(mContext, interpolator); 222 | } 223 | 224 | } 225 | -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/PageHelper.java: -------------------------------------------------------------------------------- 1 | package com.wanmei.dospy.view; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import android.app.Activity; 7 | import android.content.Context; 8 | import android.view.Gravity; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.View.OnClickListener; 12 | import android.view.ViewGroup; 13 | import android.view.WindowManager; 14 | 15 | import com.example.android_year_month_wheel.R; 16 | import com.wanmei.dospy.view.wheel.ArrayWheelAdapter; 17 | import com.wanmei.dospy.view.wheel.IWheelClickedListener; 18 | import com.wanmei.dospy.view.wheel.IWheelScrollListener; 19 | import com.wanmei.dospy.view.wheel.WheelView; 20 | 21 | public class PageHelper { 22 | 23 | private static final String TAG = "PageHelper"; 24 | 25 | View mPopupWindow; 26 | private Activity mContext; 27 | private List pageArray = new ArrayList(); 28 | private int mBottomSize; 29 | private int mItemHeight; 30 | 31 | private static final int MSG_SHOW = 2; 32 | private static final int MSG_DISMISS = 4; 33 | /** 搜索的最起始日期年份 */ 34 | public static final int FROM_YEAR = 1990; 35 | /** 搜索的最起始日期月份 */ 36 | public static final int MIN_MONTH = 1, MAX_MONTH = 12; 37 | 38 | /** 39 | * 选择滚轮item后的回调 40 | */ 41 | public interface IPageSelectedListener { 42 | void onPageSelected(int page); 43 | 44 | void onYearMonthSelected(int year, int month); 45 | } 46 | 47 | private List mYearArray = new ArrayList(), mMonthArray = new ArrayList(); 48 | private IPageSelectedListener mListener; 49 | private CustomDialog mPopMenu; 50 | private WheelView mYearWheelView, mMonthWheelView; 51 | private int mYearIndex, mMonthIndex; 52 | /**外部传进来操作滚轮的监听器*/ 53 | private View.OnClickListener mOnClickListener; 54 | 55 | public PageHelper(Activity context, View.OnClickListener listener) { 56 | mContext = context; 57 | mOnClickListener = listener; 58 | initPopMenu(); 59 | } 60 | 61 | /** 62 | * 初始化年月的双滚轮 63 | */ 64 | private void initPopMenu() { 65 | mPopMenu = new CustomDialog(mContext).createPopupWindow(); 66 | mPopMenu.setCanceledOnTouchOutside(true); 67 | final View doubleWheel = LayoutInflater.from(mContext).inflate(R.layout.layout_page_popup_view, null); 68 | WindowManager.LayoutParams layoutParams = mPopMenu.getWindow().getAttributes(); 69 | layoutParams.gravity = Gravity.BOTTOM; 70 | layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; 71 | layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; 72 | layoutParams.windowAnimations = R.style.popmenu_anim_style; 73 | doubleWheel.setOnClickListener(new OnClickListener() { 74 | @Override 75 | public void onClick(View v) { 76 | if(mOnClickListener != null){ 77 | mOnClickListener.onClick(doubleWheel); 78 | } 79 | } 80 | }); 81 | mPopMenu.setContentView(doubleWheel); 82 | 83 | 84 | mYearWheelView = (WheelView) doubleWheel.findViewById(R.id.year_page); 85 | mYearWheelView.setViewAdapter(new YearOrMonthAdapter(mContext, mYearArray, R.layout.item_wheel_view_align_right)); 86 | mYearWheelView.setVisibleItems(5); 87 | mYearWheelView.setCurrentItem(0); 88 | mYearWheelView.setShadowColor(0xFF000000, 0x88000000, 0x00000000); 89 | //可设置是否开启近大远小效果,不写下行代码则为正常展示效果 90 | mYearWheelView.setIsItemSizeChange(20, 4);//参数1:标准TextSize 参数2:TextSize每远一个Item的缩小倍数 91 | mYearWheelView.addClickingListener(new IWheelClickedListener() { 92 | @Override 93 | public void onItemClicked(WheelView wheel, int itemIndex) { 94 | mYearIndex = itemIndex; 95 | selectYearAndMonth(mYearIndex, mMonthIndex); 96 | } 97 | }); 98 | mYearWheelView.addScrollingListener(new IWheelScrollListener() { 99 | @Override 100 | public void onScrollingStarted(WheelView wheel) { 101 | } 102 | 103 | @Override 104 | public void onScrollingFinished(WheelView wheel) { 105 | mYearIndex = wheel.getCurrentItem(); 106 | } 107 | }); 108 | 109 | mMonthWheelView = (WheelView) doubleWheel.findViewById(R.id.month_page); 110 | mMonthWheelView.setViewAdapter(new YearOrMonthAdapter(mContext, mMonthArray, R.layout.item_wheel_view_align_left)); 111 | mMonthWheelView.setVisibleItems(5); 112 | mMonthWheelView.setCurrentItem(0); 113 | mMonthWheelView.setShadowColor(0xFF000000, 0x88000000, 0x00000000); 114 | //可设置是否开启近大远小效果,不写下行代码则为正常展示效果 115 | mMonthWheelView.setIsItemSizeChange(20, 4);//参数1:标准TextSize 参数2:TextSize每远一个Item的缩小倍数 116 | mMonthWheelView.addClickingListener(new IWheelClickedListener() { 117 | @Override 118 | public void onItemClicked(WheelView wheel, int itemIndex) { 119 | mMonthIndex = itemIndex; 120 | selectYearAndMonth(mYearIndex, mMonthIndex); 121 | } 122 | }); 123 | mMonthWheelView.addScrollingListener(new IWheelScrollListener() { 124 | @Override 125 | public void onScrollingStarted(WheelView wheel) { 126 | } 127 | 128 | @Override 129 | public void onScrollingFinished(WheelView wheel) { 130 | mMonthIndex = wheel.getCurrentItem(); 131 | } 132 | }); 133 | } 134 | 135 | public void selectYearAndMonth(int year, int month) { 136 | if (mListener != null) { 137 | mListener.onYearMonthSelected(year, month); 138 | mPopMenu.dismiss(); 139 | } 140 | } 141 | 142 | public void setListener(IPageSelectedListener listener) { 143 | mListener = listener; 144 | } 145 | 146 | 147 | /** 148 | * 设置年份选择的范围 149 | * @param from 150 | * @param to 151 | */ 152 | public void setYearArrange(int from, int to) { 153 | mYearArray.clear(); 154 | for (int i = from; i <= to; i++) { 155 | String prompt = mContext.getString(R.string.year_prompt, i); 156 | mYearArray.add(prompt); 157 | } 158 | } 159 | 160 | /** 161 | * 设置月份选择的范围 162 | * @param from 163 | * @param to 164 | */ 165 | public void setMonthArrange(int from, int to) { 166 | mMonthArray.clear(); 167 | for (int i = from; i <= to; i++) { 168 | String prompt = mContext.getString(R.string.month_prompt, i); 169 | mMonthArray.add(prompt); 170 | } 171 | } 172 | 173 | 174 | /** 175 | * 显示弹出滚轮 176 | * @param currentPageNum 177 | */ 178 | public void showPopup(int currentPageNum) { 179 | } 180 | 181 | /** 182 | * 显示弹出滚轮 183 | * 184 | * @param year 年份 185 | * @param month 月份 186 | */ 187 | public void showPopup(int year, int month) { 188 | if (mYearArray.size() <= 0 || mMonthArray.size() <= 0) { 189 | return; 190 | } 191 | //显示年份滚轮 192 | mYearWheelView.setViewAdapter(new YearOrMonthAdapter(mContext, mYearArray, R.layout.item_wheel_view_align_right)); 193 | mYearWheelView.setWheelBackground(R.drawable.wheel_bg_day); 194 | mYearWheelView.setWheelForeground(R.drawable.wheel_fg_day); //设置选择条目的前景 195 | mYearWheelView.setDrawShadows(false); 196 | mYearWheelView.setCurrentItem(year - FROM_YEAR); 197 | 198 | //显示月份滚轮 199 | mMonthWheelView.setViewAdapter(new YearOrMonthAdapter(mContext, mMonthArray, R.layout.item_wheel_view_align_left)); 200 | mMonthWheelView.setWheelBackground(R.drawable.wheel_bg_day); 201 | mMonthWheelView.setWheelForeground(R.drawable.wheel_fg_day); //设置选择条目的前景 202 | mMonthWheelView.setDrawShadows(false); 203 | mMonthWheelView.setCurrentItem(month); 204 | 205 | mPopMenu.show(); 206 | } 207 | 208 | /** 209 | * 显示年月的双滚轮 210 | */ 211 | public class YearOrMonthAdapter extends ArrayWheelAdapter { 212 | 213 | public YearOrMonthAdapter(Context context, List items, int itemResource) { 214 | super(context, items, itemResource); 215 | } 216 | 217 | @Override 218 | public View getItem(int index, View convertView, ViewGroup parent) { 219 | return super.getItem(index, convertView, parent); 220 | } 221 | } 222 | 223 | 224 | /** 225 | * 获得年份的序号 226 | * @return 227 | */ 228 | public int getYearIndex() { 229 | return mYearIndex; 230 | } 231 | 232 | /** 233 | * 获得月份的序号 234 | * @return 235 | */ 236 | public int getMonthIndex() { 237 | return mMonthIndex; 238 | } 239 | } -------------------------------------------------------------------------------- /src/com/wanmei/dospy/view/wheel/WheelView.java: -------------------------------------------------------------------------------- 1 | package com.wanmei.dospy.view.wheel; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | 6 | import android.content.Context; 7 | import android.database.DataSetObserver; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.drawable.Drawable; 11 | import android.graphics.drawable.GradientDrawable; 12 | import android.graphics.drawable.GradientDrawable.Orientation; 13 | import android.util.AttributeSet; 14 | import android.view.MotionEvent; 15 | import android.view.View; 16 | import android.view.ViewGroup.LayoutParams; 17 | import android.view.animation.Interpolator; 18 | import android.widget.LinearLayout; 19 | import android.widget.TextView; 20 | 21 | import com.example.android_year_month_wheel.R; 22 | 23 | public final class WheelView extends View { 24 | private static final int DEF_VISIBLE_ITEMS = 5; 25 | private int mVisibleItems = DEF_VISIBLE_ITEMS; 26 | private int[] SHADOWS_COLORS = new int[] { 0xFF111111, 27 | 0x00AAAAAA, 0x00AAAAAA }; 28 | private int mWheelBackground = R.drawable.wheel_bg_day; 29 | private int mWheelForeground = R.drawable.wheel_val; 30 | private Drawable mCenterDrawable; 31 | 32 | private IWheelViewAdapter mViewAdapter; 33 | private WheelRecycle mRecycle = new WheelRecycle(this); 34 | private LinearLayout mItemsLayout; 35 | private int mCurrentItem = 0; 36 | boolean mIsCyclic = false; 37 | 38 | private List mScrollingListeners = new LinkedList(); 39 | private List mChangingListeners = new LinkedList(); 40 | private List mClickingListeners = new LinkedList(); 41 | 42 | private WheelScroller mWheelScroller; 43 | private boolean mIsScrollingPerformed; 44 | private boolean mIsItemSizeChange; 45 | private int mScrollingOffset; 46 | private int mItemNormalTextSize; 47 | private int mReduceSizeMultiplePreItem; 48 | 49 | private WheelScroller.IScrollingListener mIScrollingListener = new WheelScroller.IScrollingListener() { 50 | @Override 51 | public void onStarted() { 52 | mIsScrollingPerformed = true; 53 | notifyScrollingListenersAboutStart(); 54 | } 55 | 56 | @Override 57 | public void onScroll(int distance) { 58 | doScroll(distance); 59 | int height = getHeight(); 60 | if (mScrollingOffset > height) { 61 | mScrollingOffset = height; 62 | mWheelScroller.stopScrolling(); 63 | } else if (mScrollingOffset < -height) { 64 | mScrollingOffset = -height; 65 | mWheelScroller.stopScrolling(); 66 | } 67 | } 68 | 69 | @Override 70 | public void onFinished() { 71 | if (mIsScrollingPerformed) { 72 | notifyScrollingListenersAboutEnd(); 73 | mIsScrollingPerformed = false; 74 | } 75 | mScrollingOffset = 0; 76 | invalidate(); 77 | } 78 | 79 | @Override 80 | public void onJustify() { 81 | if (Math.abs(mScrollingOffset) > WheelScroller.MIN_DELTA_FOR_SCROLLING) { 82 | mWheelScroller.scroll(mScrollingOffset, 0); 83 | } 84 | } 85 | }; 86 | 87 | public WheelView(Context context, AttributeSet attrs, int defStyle) { 88 | super(context, attrs, defStyle); 89 | initData(context); 90 | } 91 | 92 | public WheelView(Context context, AttributeSet attrs) { 93 | super(context, attrs); 94 | initData(context); 95 | } 96 | 97 | public WheelView(Context context) { 98 | super(context); 99 | initData(context); 100 | } 101 | 102 | private void initData(Context context) { 103 | mWheelScroller = new WheelScroller(context, mIScrollingListener); 104 | } 105 | 106 | public void setWheelBackground(int resource) { 107 | mWheelBackground = resource; 108 | setBackgroundResource(mWheelBackground); 109 | } 110 | 111 | public void setWheelForeground(int resource) { 112 | mWheelForeground = resource; 113 | mCenterDrawable = getContext().getResources().getDrawable(mWheelForeground); 114 | } 115 | 116 | public void setShadowColor(int start, int middle, int end) { 117 | SHADOWS_COLORS = new int[] { start, middle, end }; 118 | } 119 | 120 | /** 121 | * Sets view adapter. Usually new adapters contain different views, so 122 | * it needs to rebuild view by calling measure(). 123 | * 124 | * @param viewAdapter the view adapter 125 | */ 126 | public void setViewAdapter(IWheelViewAdapter viewAdapter) { 127 | if (mViewAdapter != null) { 128 | mViewAdapter.unregisterDataSetObserver(dataObserver); 129 | } 130 | mViewAdapter = viewAdapter; 131 | if (mViewAdapter != null) { 132 | mViewAdapter.registerDataSetObserver(dataObserver); 133 | } 134 | invalidateWheel(true); 135 | } 136 | 137 | /** 138 | * Invalidates wheel 139 | * 140 | * @param clearCaches if true then cached views will be clear 141 | */ 142 | public void invalidateWheel(boolean clearCaches) { 143 | if (clearCaches) { 144 | mRecycle.clearAll(); 145 | if (mItemsLayout != null) { 146 | mItemsLayout.removeAllViews(); 147 | } 148 | mScrollingOffset = 0; 149 | } else if (mItemsLayout != null) { 150 | // cache all items 151 | mRecycle.recycleItems(mItemsLayout, firstItem, new ItemsRange()); 152 | } 153 | 154 | invalidate(); 155 | } 156 | 157 | 158 | /** 159 | * Sets the current item w/o animation. Does nothing when index is wrong. 160 | * 161 | * @param index the item index 162 | */ 163 | public void setCurrentItem(int index) { 164 | setCurrentItem(index, false); 165 | } 166 | 167 | /** 168 | * Sets the current item. Does nothing when index is wrong. 169 | * 170 | * @param index the item index 171 | * @param animated the animation flag 172 | */ 173 | public void setCurrentItem(int index, boolean animated) { 174 | if (mViewAdapter == null || mViewAdapter.getItemsCount() == 0) { 175 | return; // throw? 176 | } 177 | int itemCount = mViewAdapter.getItemsCount(); 178 | if (index < 0 || index >= itemCount) { 179 | if (mIsCyclic) { 180 | while (index < 0) { 181 | index += itemCount; 182 | } 183 | index %= itemCount; 184 | } else { 185 | return; // throw? 186 | } 187 | } 188 | if (index != mCurrentItem) { 189 | if (animated) { 190 | int itemsToScroll = index - mCurrentItem; 191 | if (mIsCyclic) { 192 | int scroll = itemCount + Math.min(index, mCurrentItem) - Math.max(index, mCurrentItem); 193 | if (scroll < Math.abs(itemsToScroll)) { 194 | itemsToScroll = itemsToScroll < 0 ? scroll : -scroll; 195 | } 196 | } 197 | scroll(itemsToScroll, 0); 198 | } else { 199 | mScrollingOffset = 0; 200 | int old = mCurrentItem; 201 | mCurrentItem = index; 202 | notifyChangingListeners(old, mCurrentItem); 203 | invalidate(); 204 | } 205 | } 206 | } 207 | 208 | /** 209 | * Sets the desired count of visible items. 210 | * Actual amount of visible items depends on wheel layout parameters. 211 | * To apply changes and rebuild view call measure(). 212 | * 213 | * @param count the desired count for visible items 214 | */ 215 | public void setVisibleItems(int count) { 216 | mVisibleItems = count; 217 | } 218 | 219 | 220 | /** 221 | * Notifies listeners about starting scrolling 222 | */ 223 | protected void notifyScrollingListenersAboutStart() { 224 | for (IWheelScrollListener listener : mScrollingListeners) { 225 | listener.onScrollingStarted(this); 226 | } 227 | } 228 | 229 | /** 230 | * Top and bottom items offset (to hide that) 231 | */ 232 | private static final int ITEM_OFFSET_PERCENT = 0; 233 | 234 | /** 235 | * Left and right padding value 236 | */ 237 | private static final int PADDING = 10; 238 | 239 | // Item height 240 | private int itemHeight = 0; 241 | 242 | 243 | // Shadows drawables 244 | private GradientDrawable topShadow; 245 | private GradientDrawable bottomShadow; 246 | 247 | // Draw Shadows 248 | private boolean drawShadows = true; 249 | 250 | // The number of first item in layout 251 | private int firstItem; 252 | 253 | 254 | /** 255 | * Set the the specified scrolling interpolator 256 | * @param interpolator the interpolator 257 | */ 258 | public void setInterpolator(Interpolator interpolator) { 259 | mWheelScroller.setInterpolator(interpolator); 260 | } 261 | 262 | /** 263 | * Gets view adapter 264 | * @return the view adapter 265 | */ 266 | public IWheelViewAdapter getViewAdapter() { 267 | return mViewAdapter; 268 | } 269 | 270 | // Adapter listener 271 | private DataSetObserver dataObserver = new DataSetObserver() { 272 | @Override 273 | public void onChanged() { 274 | invalidateWheel(false); 275 | } 276 | 277 | @Override 278 | public void onInvalidated() { 279 | invalidateWheel(true); 280 | } 281 | }; 282 | 283 | 284 | /** 285 | * Adds wheel changing listener 286 | * @param listener the listener 287 | */ 288 | public void addChangingListener(IWheelChangedListener listener) { 289 | mChangingListeners.add(listener); 290 | } 291 | 292 | /** 293 | * Removes wheel changing listener 294 | * @param listener the listener 295 | */ 296 | public void removeChangingListener(IWheelChangedListener listener) { 297 | mChangingListeners.remove(listener); 298 | } 299 | 300 | /** 301 | * Notifies changing listeners 302 | * @param oldValue the old wheel value 303 | * @param newValue the new wheel value 304 | */ 305 | protected void notifyChangingListeners(int oldValue, int newValue) { 306 | for (IWheelChangedListener listener : mChangingListeners) { 307 | listener.onChanged(this, oldValue, newValue); 308 | } 309 | } 310 | 311 | /** 312 | * Adds wheel scrolling listener 313 | * @param listener the listener 314 | */ 315 | public void addScrollingListener(IWheelScrollListener listener) { 316 | mScrollingListeners.add(listener); 317 | } 318 | 319 | /** 320 | * Removes wheel scrolling listener 321 | * @param listener the listener 322 | */ 323 | public void removeScrollingListener(IWheelScrollListener listener) { 324 | mScrollingListeners.remove(listener); 325 | } 326 | 327 | /** 328 | * Notifies listeners about ending scrolling 329 | */ 330 | protected void notifyScrollingListenersAboutEnd() { 331 | for (IWheelScrollListener listener : mScrollingListeners) { 332 | listener.onScrollingFinished(this); 333 | } 334 | } 335 | 336 | /** 337 | * Adds wheel clicking listener 338 | * @param listener the listener 339 | */ 340 | public void addClickingListener(IWheelClickedListener listener) { 341 | mClickingListeners.add(listener); 342 | } 343 | 344 | /** 345 | * Removes wheel clicking listener 346 | * @param listener the listener 347 | */ 348 | public void removeClickingListener(IWheelClickedListener listener) { 349 | mClickingListeners.remove(listener); 350 | } 351 | 352 | /** 353 | * Notifies listeners about clicking 354 | */ 355 | protected void notifyClickListenersAboutClick(int item) { 356 | for (IWheelClickedListener listener : mClickingListeners) { 357 | listener.onItemClicked(this, item); 358 | } 359 | } 360 | 361 | 362 | /** 363 | * Tests if wheel is cyclic. That means before the 1st item there is shown the last one 364 | * @return true if wheel is cyclic 365 | */ 366 | public boolean isCyclic() { 367 | return mIsCyclic; 368 | } 369 | 370 | /** 371 | * Set wheel cyclic flag 372 | * @param isCyclic the flag to set 373 | */ 374 | public void setCyclic(boolean isCyclic) { 375 | this.mIsCyclic = isCyclic; 376 | invalidateWheel(false); 377 | } 378 | 379 | 380 | /** 381 | * Initializes resources 382 | */ 383 | private void initResourcesIfNecessary() { 384 | if (mCenterDrawable == null) { 385 | mCenterDrawable = getContext().getResources().getDrawable(mWheelForeground); 386 | } 387 | 388 | if (topShadow == null) { 389 | topShadow = new GradientDrawable(Orientation.TOP_BOTTOM, SHADOWS_COLORS); 390 | } 391 | 392 | if (bottomShadow == null) { 393 | bottomShadow = new GradientDrawable(Orientation.BOTTOM_TOP, SHADOWS_COLORS); 394 | } 395 | 396 | setBackgroundResource(mWheelBackground); 397 | } 398 | 399 | /** 400 | * Calculates desired height for layout 401 | * @param layout the source layout 402 | * @return the desired layout height 403 | */ 404 | private int getDesiredHeight(LinearLayout layout) { 405 | if (layout != null && layout.getChildAt(0) != null) { 406 | itemHeight = layout.getChildAt(0).getMeasuredHeight(); 407 | } 408 | 409 | int desired = itemHeight * mVisibleItems - itemHeight * ITEM_OFFSET_PERCENT / 50; 410 | 411 | return Math.max(desired, getSuggestedMinimumHeight()); 412 | } 413 | 414 | /** 415 | * Returns height of wheel item 416 | * @return the item height 417 | */ 418 | private int getItemHeight() { 419 | if (itemHeight != 0) { 420 | return itemHeight; 421 | } 422 | 423 | if (mItemsLayout != null && mItemsLayout.getChildAt(0) != null) { 424 | itemHeight = mItemsLayout.getChildAt(0).getHeight(); 425 | return itemHeight; 426 | } 427 | 428 | return getHeight() / mVisibleItems; 429 | } 430 | 431 | /** 432 | * Calculates control width and creates text layouts 433 | * @param widthSize the input layout width 434 | * @param mode the layout mode 435 | * @return the calculated control width 436 | */ 437 | private int calculateLayoutWidth(int widthSize, int mode) { 438 | initResourcesIfNecessary(); 439 | 440 | // TODO: make it static 441 | mItemsLayout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); 442 | mItemsLayout.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.UNSPECIFIED), 443 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 444 | int width = mItemsLayout.getMeasuredWidth(); 445 | 446 | if (mode == MeasureSpec.EXACTLY) { 447 | width = widthSize; 448 | } else { 449 | width += 2 * PADDING; 450 | 451 | // Check against our minimum width 452 | width = Math.max(width, getSuggestedMinimumWidth()); 453 | 454 | if (mode == MeasureSpec.AT_MOST && widthSize < width) { 455 | width = widthSize; 456 | } 457 | } 458 | 459 | mItemsLayout.measure(MeasureSpec.makeMeasureSpec(width - 2 * PADDING, MeasureSpec.EXACTLY), 460 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 461 | 462 | return width; 463 | } 464 | 465 | @Override 466 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 467 | int widthMode = MeasureSpec.getMode(widthMeasureSpec); 468 | int heightMode = MeasureSpec.getMode(heightMeasureSpec); 469 | int widthSize = MeasureSpec.getSize(widthMeasureSpec); 470 | int heightSize = MeasureSpec.getSize(heightMeasureSpec); 471 | 472 | buildViewForMeasuring(); 473 | 474 | int width = calculateLayoutWidth(widthSize, widthMode); 475 | 476 | int height; 477 | if (heightMode == MeasureSpec.EXACTLY) { 478 | height = heightSize; 479 | } else { 480 | height = getDesiredHeight(mItemsLayout); 481 | 482 | if (heightMode == MeasureSpec.AT_MOST) { 483 | height = Math.min(height, heightSize); 484 | } 485 | } 486 | 487 | setMeasuredDimension(width, height); 488 | } 489 | 490 | @Override 491 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 492 | layout(r - l, b - t); 493 | } 494 | 495 | /** 496 | * Sets layouts width and height 497 | * 498 | * @param width the layout width 499 | * @param height the layout height 500 | */ 501 | private void layout(int width, int height) { 502 | int itemsWidth = width - 2 * PADDING; 503 | 504 | mItemsLayout.layout(0, 0, itemsWidth, height); 505 | } 506 | 507 | @Override 508 | protected void onDraw(Canvas canvas) { 509 | super.onDraw(canvas); 510 | if (mViewAdapter != null && mViewAdapter.getItemsCount() > 0) { 511 | updateView(); 512 | drawCenterRect(canvas); 513 | drawItems(canvas); 514 | } 515 | if (drawShadows) { 516 | drawShadows(canvas); 517 | } 518 | } 519 | 520 | /** 521 | * Draws shadows on top and bottom of control 522 | * 523 | * @param canvas the canvas for drawing 524 | */ 525 | private void drawShadows(Canvas canvas) { 526 | int height = (int) (2.0 * getItemHeight()); 527 | topShadow.setBounds(0, 0, getWidth(), height); 528 | topShadow.draw(canvas); 529 | 530 | bottomShadow.setBounds(0, getHeight() - height, getWidth(), getHeight()); 531 | bottomShadow.draw(canvas); 532 | } 533 | 534 | /** 535 | * Draws items 536 | * 537 | * @param canvas the canvas for drawing 538 | */ 539 | private void drawItems(Canvas canvas) { 540 | canvas.save(); 541 | 542 | int top = (mCurrentItem - firstItem) * getItemHeight() + (getItemHeight() - getHeight()) / 2; 543 | canvas.translate(PADDING, -top + mScrollingOffset); 544 | mItemsLayout.draw(canvas); 545 | canvas.restore(); 546 | } 547 | 548 | /** 549 | * Draws rect for current value 550 | * 551 | * @param canvas the canvas for drawing 552 | */ 553 | private void drawCenterRect(Canvas canvas) { 554 | int center = getHeight() / 2; 555 | int offset = (int) (getItemHeight() / 2 * 1.2); 556 | mCenterDrawable.setBounds(0, center - offset, getWidth(), center + offset); 557 | mCenterDrawable.draw(canvas); 558 | } 559 | 560 | @Override 561 | public boolean onTouchEvent(MotionEvent event) { 562 | if (!isEnabled() || getViewAdapter() == null) { 563 | return true; 564 | } 565 | 566 | switch (event.getAction()) { 567 | case MotionEvent.ACTION_MOVE: 568 | if (getParent() != null) { 569 | getParent().requestDisallowInterceptTouchEvent(true); 570 | } 571 | break; 572 | 573 | case MotionEvent.ACTION_UP: 574 | //滑动判定 575 | if (!mIsScrollingPerformed) { 576 | int distance = (int) event.getY() - getHeight() / 2; 577 | if (distance > 0) { 578 | distance += getItemHeight() / 2; 579 | } else { 580 | distance -= getItemHeight() / 2; 581 | } 582 | int items = distance / getItemHeight(); 583 | if (isValidItemIndex(mCurrentItem + items)) { 584 | notifyClickListenersAboutClick(mCurrentItem + items); 585 | } 586 | } 587 | break; 588 | } 589 | 590 | return mWheelScroller.onTouchEvent(event); 591 | } 592 | 593 | /** 594 | * Scrolls the wheel 595 | * 596 | * @param delta the scrolling value 597 | */ 598 | private void doScroll(int delta) { 599 | mScrollingOffset += delta; 600 | 601 | int itemHeight = getItemHeight(); 602 | int count = mScrollingOffset / itemHeight; 603 | 604 | int pos = mCurrentItem - count; 605 | int itemCount = mViewAdapter.getItemsCount(); 606 | 607 | int fixPos = mScrollingOffset % itemHeight; 608 | if (Math.abs(fixPos) <= itemHeight / 2) { 609 | fixPos = 0; 610 | } 611 | if (mIsCyclic && itemCount > 0) { 612 | if (fixPos > 0) { 613 | pos--; 614 | count++; 615 | } else if (fixPos < 0) { 616 | pos++; 617 | count--; 618 | } 619 | // fix position by rotating 620 | while (pos < 0) { 621 | pos += itemCount; 622 | } 623 | pos %= itemCount; 624 | } else { 625 | // 626 | if (pos < 0) { 627 | count = mCurrentItem; 628 | pos = 0; 629 | } else if (pos >= itemCount) { 630 | count = mCurrentItem - itemCount + 1; 631 | pos = itemCount - 1; 632 | } else if (pos > 0 && fixPos > 0) { 633 | pos--; 634 | count++; 635 | } else if (pos < itemCount - 1 && fixPos < 0) { 636 | pos++; 637 | count--; 638 | } 639 | } 640 | 641 | int offset = mScrollingOffset; 642 | if (pos != mCurrentItem) { 643 | setCurrentItem(pos, false); 644 | } else { 645 | invalidate(); 646 | } 647 | 648 | // update offset 649 | mScrollingOffset = offset - count * itemHeight; 650 | if (mScrollingOffset > getHeight()) { 651 | if (getHeight() <= 0) { 652 | mScrollingOffset = 0; 653 | } else { 654 | mScrollingOffset = mScrollingOffset % getHeight() + getHeight(); 655 | } 656 | } 657 | } 658 | 659 | public void scroll(int itemsToScroll, int time) { 660 | int distance = itemsToScroll * getItemHeight() - mScrollingOffset; 661 | mWheelScroller.scroll(distance, time); 662 | } 663 | 664 | /** 665 | * Calculates range for wheel items 666 | * 667 | * @return the items range 668 | */ 669 | private ItemsRange getItemsRange() { 670 | if (getItemHeight() == 0) { 671 | return null; 672 | } 673 | 674 | int first = mCurrentItem; 675 | int count = 1; 676 | 677 | while (count * getItemHeight() < getHeight()) { 678 | first--; 679 | count += 2; // top + bottom items 680 | } 681 | 682 | if (mScrollingOffset != 0) { 683 | if (mScrollingOffset > 0) { 684 | first--; 685 | } 686 | count++; 687 | 688 | // process empty items above the first or below the second 689 | int emptyItems = mScrollingOffset / getItemHeight(); 690 | first -= emptyItems; 691 | count += Math.asin(emptyItems); 692 | } 693 | return new ItemsRange(first, count); 694 | } 695 | 696 | /** 697 | * Rebuilds wheel items if necessary. Caches all unused items. 698 | * 699 | * @return true if items are rebuilt 700 | */ 701 | private boolean rebuildItems() { 702 | boolean updated = false; 703 | ItemsRange range = getItemsRange(); 704 | if (mItemsLayout != null) { 705 | int first = mRecycle.recycleItems(mItemsLayout, firstItem, range); 706 | updated = firstItem != first; 707 | firstItem = first; 708 | } else { 709 | createItemsLayout(); 710 | updated = true; 711 | } 712 | 713 | if (!updated) { 714 | updated = firstItem != range.getFirst() || mItemsLayout.getChildCount() != range.getCount(); 715 | } 716 | 717 | if (firstItem > range.getFirst() && firstItem <= range.getLast()) { 718 | for (int i = firstItem - 1; i >= range.getFirst(); i--) { 719 | if (!addViewItem(i, true)) { 720 | break; 721 | } 722 | firstItem = i; 723 | } 724 | } else { 725 | firstItem = range.getFirst(); 726 | } 727 | 728 | int first = firstItem; 729 | for (int i = mItemsLayout.getChildCount(); i < range.getCount(); i++) { 730 | if (!addViewItem(firstItem + i, false) && mItemsLayout.getChildCount() == 0) { 731 | first++; 732 | } 733 | } 734 | firstItem = first; 735 | if(mIsItemSizeChange) { 736 | for(int i = 0; i < mItemsLayout.getChildCount(); i++) { 737 | View view = mItemsLayout.getChildAt(i); 738 | int size = Math.abs((mCurrentItem - firstItem) - i) * mReduceSizeMultiplePreItem; 739 | TextView textView = mViewAdapter.getTextView(view, mViewAdapter.getTextViewItemResourceId()); 740 | textView.setTextSize(mItemNormalTextSize - size); 741 | int alpha = 255 - Math.abs((mCurrentItem - firstItem) - i) * 80; 742 | // if(AppConfiguration.getInstance(getContext()).isNightMode()){ 743 | // textView.setTextColor(Color.argb(alpha, 255, 255, 255)); //设置文本的透明度 744 | // }else{ 745 | textView.setTextColor(Color.argb(alpha, 0, 0, 0)); 746 | // } 747 | } 748 | } 749 | 750 | return updated; 751 | } 752 | 753 | /** 754 | * Updates view. Rebuilds items and label if necessary, recalculate items sizes. 755 | */ 756 | private void updateView() { 757 | if (rebuildItems()) { 758 | calculateLayoutWidth(getWidth(), MeasureSpec.EXACTLY); 759 | layout(getWidth(), getHeight()); 760 | } 761 | } 762 | 763 | /** 764 | * Creates item layouts if necessary 765 | */ 766 | private void createItemsLayout() { 767 | if (mItemsLayout == null) { 768 | mItemsLayout = new LinearLayout(getContext()); 769 | mItemsLayout.setOrientation(LinearLayout.VERTICAL); 770 | } 771 | } 772 | 773 | /** 774 | * Builds view for measuring 775 | */ 776 | private void buildViewForMeasuring() { 777 | // clear all items 778 | if (mItemsLayout != null) { 779 | mRecycle.recycleItems(mItemsLayout, firstItem, new ItemsRange()); 780 | } else { 781 | createItemsLayout(); 782 | } 783 | 784 | // add views 785 | // all items must be included to measure width correctly 786 | // PS: only add VisibleItems is more efficient —— 2015.5.20 787 | for (int i = mVisibleItems - 1; i >= 0; i--) { 788 | if (addViewItem(i, true)) { 789 | firstItem = i; 790 | } 791 | } 792 | } 793 | 794 | /** 795 | * Adds view for item to items layout 796 | * 797 | * @param index the item index 798 | * @param first the flag indicates if view should be first 799 | * @return true if corresponding item exists and is added 800 | */ 801 | private boolean addViewItem(int index, boolean first) { 802 | View view = getItemView(index); 803 | if (view != null) { 804 | if (first) { 805 | mItemsLayout.addView(view, 0); 806 | } else { 807 | mItemsLayout.addView(view); 808 | } 809 | 810 | return true; 811 | } 812 | 813 | return false; 814 | } 815 | 816 | /** 817 | * Checks whether intem index is valid 818 | * 819 | * @param index the item index 820 | * @return true if item index is not out of bounds or the wheel is cyclic 821 | */ 822 | private boolean isValidItemIndex(int index) { 823 | return mViewAdapter != null && mViewAdapter.getItemsCount() > 0 && 824 | (mIsCyclic || index >= 0 && index < mViewAdapter.getItemsCount()); 825 | } 826 | 827 | /** 828 | * Returns view for specified item 829 | * 830 | * @param index the item index 831 | * @return item view or empty view if index is out of bounds 832 | */ 833 | private View getItemView(int index) { 834 | if (mViewAdapter == null || mViewAdapter.getItemsCount() == 0) { 835 | return null; 836 | } 837 | int count = mViewAdapter.getItemsCount(); 838 | if (!isValidItemIndex(index)) { 839 | return mViewAdapter.getEmptyItem(mRecycle.getEmptyItem(), mItemsLayout); 840 | } else { 841 | while (index < 0) { 842 | index = count + index; 843 | } 844 | } 845 | 846 | index %= count; 847 | 848 | View itemView = mViewAdapter.getItem(index, mRecycle.getItem(), mItemsLayout); 849 | if(mIsItemSizeChange) { 850 | TextView textView = mViewAdapter.getTextView(itemView, mViewAdapter.getTextViewItemResourceId()); 851 | textView.setTextSize(mItemNormalTextSize); 852 | } 853 | 854 | return itemView; 855 | } 856 | 857 | /** 858 | * Stops scrolling 859 | */ 860 | public void stopScrolling() { 861 | mWheelScroller.stopScrolling(); 862 | } 863 | 864 | public void setIsItemSizeChange(int normalSize, int reduceSize) { 865 | this.mIsItemSizeChange = true; 866 | this.mItemNormalTextSize = normalSize; 867 | this.mReduceSizeMultiplePreItem = reduceSize; 868 | } 869 | 870 | public void setDrawShadows(boolean drawShadows) { 871 | this.drawShadows = drawShadows; 872 | } 873 | 874 | /** 875 | * 获取当前的item 876 | * @return 877 | */ 878 | public int getCurrentItem() { 879 | return mCurrentItem; 880 | } 881 | } --------------------------------------------------------------------------------