Android-仿iPhone滚轮控件效果


在坛子里看到的,自己弄个Android-仿iPhone滚轮控件效果:


这个滚动的WheelView

  1. /* 
  2.  *  Android Wheel Control. 
  3.  *  https://code.google.com/p/android-wheel/ 
  4.  *   
  5.  *  Copyright 2010 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 kankan.wheel.widget;  
  21.   
  22. import java.util.LinkedList;  
  23. import java.util.List;  
  24.   
  25. import android.content.Context;  
  26. import android.graphics.Canvas;  
  27. import android.graphics.Paint;  
  28. import android.graphics.Rect;  
  29. import android.graphics.drawable.Drawable;  
  30. import android.graphics.drawable.GradientDrawable;  
  31. import android.graphics.drawable.GradientDrawable.Orientation;  
  32. import android.os.Handler;  
  33. import android.os.Message;  
  34. import android.text.Layout;  
  35. import android.text.StaticLayout;  
  36. import android.text.TextPaint;  
  37. import android.util.AttributeSet;  
  38. import android.util.FloatMath;  
  39. import android.view.GestureDetector;  
  40. import android.view.GestureDetector.SimpleOnGestureListener;  
  41. import android.view.MotionEvent;  
  42. import android.view.View;  
  43. import android.view.animation.Interpolator;  
  44. import android.widget.Scroller;  
  45.   
  46. import com.shao.pwd.R;  
  47.   
  48. /** 
  49.  * Numeric wheel view. 
  50.  *  
  51.  * @author Yuri Kanivets 
  52.  */  
  53. public class WheelView extends View {  
  54.     /** Scrolling duration */  
  55.     private static final int SCROLLING_DURATION = 400;  
  56.   
  57.     /** Minimum delta for scrolling */  
  58.     private static final int MIN_DELTA_FOR_SCROLLING = 1;  
  59.   
  60.     /** Current value & label text color */  
  61.     private static final int VALUE_TEXT_COLOR = 0xF0000000;  
  62.   
  63.     /** Items text color */  
  64.     private static final int ITEMS_TEXT_COLOR = 0xFF000000;  
  65.   
  66.     /** Top and bottom shadows colors */  
  67.     private static final int[] SHADOWS_COLORS = new int[] { 0xFF111111,  
  68.             0x00AAAAAA0x00AAAAAA };  
  69.   
  70.     /** Additional items height (is added to standard text item height) */  
  71.     private static final int ADDITIONAL_ITEM_HEIGHT = 15;  
  72.   
  73.     /** Text size */  
  74.     private static final int TEXT_SIZE = 24;  
  75.   
  76.     /** Top and bottom items offset (to hide that) */  
  77.     private static final int ITEM_OFFSET = TEXT_SIZE / 5;  
  78.   
  79.     /** Additional width for items layout */  
  80.     private static final int ADDITIONAL_ITEMS_SPACE = 10;  
  81.   
  82.     /** Label offset */  
  83.     private static final int LABEL_OFFSET = 8;  
  84.   
  85.     /** Left and right padding value */  
  86.     private static final int PADDING = 10;  
  87.   
  88.     /** Default count of visible items */  
  89.     private static final int DEF_VISIBLE_ITEMS = 5;  
  90.   
  91.     // Wheel Values   
  92.     private WheelAdapter adapter = null;  
  93.     private int currentItem = 0;  
  94.       
  95.     // Widths   
  96.     private int itemsWidth = 0;  
  97.     private int labelWidth = 0;  
  98.   
  99.     // Count of visible items   
  100.     private int visibleItems = DEF_VISIBLE_ITEMS;  
  101.       
  102.     // Item height   
  103.     private int itemHeight = 0;  
  104.   
  105.     // Text paints   
  106.     private TextPaint itemsPaint;  
  107.     private TextPaint valuePaint;  
  108.   
  109.     // Layouts   
  110.     private StaticLayout itemsLayout;  
  111.     private StaticLayout labelLayout;  
  112.     private StaticLayout valueLayout;  
  113.   
  114.     // Label & background   
  115.     private String label;  
  116.     private Drawable centerDrawable;  
  117.   
  118.     // Shadows drawables   
  119.     private GradientDrawable topShadow;  
  120.     private GradientDrawable bottomShadow;  
  121.   
  122.     // Scrolling   
  123.     private boolean isScrollingPerformed;   
  124.     private int scrollingOffset;  
  125.   
  126.     // Scrolling animation   
  127.     private GestureDetector gestureDetector;  
  128.     private Scroller scroller;  
  129.     private int lastScrollY;  
  130.   
  131.     // Cyclic   
  132.     boolean isCyclic = false;  
  133.       
  134.     // Listeners   
  135.     private List<OnWheelChangedListener> changingListeners = new LinkedList<OnWheelChangedListener>();  
  136.     private List<OnWheelScrollListener> scrollingListeners = new LinkedList<OnWheelScrollListener>();  
  137.   
  138.     /** 
  139.      * Constructor 
  140.      */  
  141.     public WheelView(Context context, AttributeSet attrs, int defStyle) {  
  142.         super(context, attrs, defStyle);  
  143.         initData(context);  
  144.     }  
  145.   
  146.     /** 
  147.      * Constructor 
  148.      */  
  149.     public WheelView(Context context, AttributeSet attrs) {  
  150.         super(context, attrs);  
  151.         initData(context);  
  152.     }  
  153.   
  154.     /** 
  155.      * Constructor 
  156.      */  
  157.     public WheelView(Context context) {  
  158.         super(context);  
  159.         initData(context);  
  160.     }  
  161.       
  162.     /** 
  163.      * Initializes class data 
  164.      * @param context the context 
  165.      */  
  166.     private void initData(Context context) {  
  167.         gestureDetector = new GestureDetector(context, gestureListener);  
  168.         gestureDetector.setIsLongpressEnabled(false);  
  169.           
  170.         scroller = new Scroller(context);  
  171.     }  
  172.       
  173.     /** 
  174.      * Gets wheel adapter 
  175.      * @return the adapter 
  176.      */  
  177.     public WheelAdapter getAdapter() {  
  178.         return adapter;  
  179.     }  
  180.       
  181.     /** 
  182.      * Sets wheel adapter 
  183.      * @param adapter the new wheel adapter 
  184.      */  
  185.     public void setAdapter(WheelAdapter adapter) {  
  186.         this.adapter = adapter;  
  187.         invalidateLayouts();  
  188.         invalidate();  
  189.     }  
  190.       
  191.     /** 
  192.      * Set the the specified scrolling interpolator 
  193.      * @param interpolator the interpolator 
  194.      */  
  195.     public void setInterpolator(Interpolator interpolator) {  
  196.         scroller.forceFinished(true);  
  197.         scroller = new Scroller(getContext(), interpolator);  
  198.     }  
  199.       
  200.     /** 
  201.      * Gets count of visible items 
  202.      *  
  203.      * @return the count of visible items 
  204.      */  
  205.     public int getVisibleItems() {  
  206.         return visibleItems;  
  207.     }  
  208.   
  209.     /** 
  210.      * Sets count of visible items 
  211.      *  
  212.      * @param count 
  213.      *            the new count 
  214.      */  
  215.     public void setVisibleItems(int count) {  
  216.         visibleItems = count;  
  217.         invalidate();  
  218.     }  
  219.   
  220.     /** 
  221.      * Gets label 
  222.      *  
  223.      * @return the label 
  224.      */  
  225.     public String getLabel() {  
  226.         return label;  
  227.     }  
  228.   
  229.     /** 
  230.      * Sets label 
  231.      *  
  232.      * @param newLabel 
  233.      *            the label to set 
  234.      */  
  235.     public void setLabel(String newLabel) {  
  236.         if (label == null || !label.equals(newLabel)) {  
  237.             label = newLabel;  
  238.             labelLayout = null;  
  239.             invalidate();  
  240.         }  
  241.     }  
  242.       
  243.     /** 
  244.      * Adds wheel changing listener 
  245.      * @param listener the listener  
  246.      */  
  247.     public void addChangingListener(OnWheelChangedListener listener) {  
  248.         changingListeners.add(listener);  
  249.     }  
  250.   
  251.     /** 
  252.      * Removes wheel changing listener 
  253.      * @param listener the listener 
  254.      */  
  255.     public void removeChangingListener(OnWheelChangedListener listener) {  
  256.         changingListeners.remove(listener);  
  257.     }  
  258.       
  259.     /** 
  260.      * Notifies changing listeners 
  261.      * @param oldValue the old wheel value 
  262.      * @param newValue the new wheel value 
  263.      */  
  264.     protected void notifyChangingListeners(int oldValue, int newValue) {  
  265.         for (OnWheelChangedListener listener : changingListeners) {  
  266.             listener.onChanged(this, oldValue, newValue);  
  267.         }  
  268.     }  
  269.   
  270.     /** 
  271.      * Adds wheel scrolling listener 
  272.      * @param listener the listener  
  273.      */  
  274.     public void addScrollingListener(OnWheelScrollListener listener) {  
  275.         scrollingListeners.add(listener);  
  276.     }  
  277.   
  278.     /** 
  279.      * Removes wheel scrolling listener 
  280.      * @param listener the listener 
  281.      */  
  282.     public void removeScrollingListener(OnWheelScrollListener listener) {  
  283.         scrollingListeners.remove(listener);  
  284.     }  
  285.       
  286.     /** 
  287.      * Notifies listeners about starting scrolling 
  288.      */  
  289.     protected void notifyScrollingListenersAboutStart() {  
  290.         for (OnWheelScrollListener listener : scrollingListeners) {  
  291.             listener.onScrollingStarted(this);  
  292.         }  
  293.     }  
  294.   
  295.     /** 
  296.      * Notifies listeners about ending scrolling 
  297.      */  
  298.     protected void notifyScrollingListenersAboutEnd() {  
  299.         for (OnWheelScrollListener listener : scrollingListeners) {  
  300.             listener.onScrollingFinished(this);  
  301.         }  
  302.     }  
  303.   
  304.     /** 
  305.      * Gets current value 
  306.      *  
  307.      * @return the current value 
  308.      */  
  309.     public int getCurrentItem() {  
  310.         return currentItem;  
  311.     }  
  312.   
  313.     /** 
  314.      * Sets the current item. Does nothing when index is wrong. 
  315.      *  
  316.      * @param index the item index 
  317.      * @param animated the animation flag 
  318.      */  
  319.     public void setCurrentItem(int index, boolean animated) {  
  320.         if (adapter == null || adapter.getItemsCount() == 0) {  
  321.             return// throw?   
  322.         }  
  323.         if (index < 0 || index >= adapter.getItemsCount()) {  
  324.             if (isCyclic) {  
  325.                 while (index < 0) {  
  326.                     index += adapter.getItemsCount();  
  327.                 }  
  328.                 index %= adapter.getItemsCount();  
  329.             } else{  
  330.                 return// throw?   
  331.             }  
  332.         }  
  333.         if (index != currentItem) {  
  334.             if (animated) {  
  335.                 scroll(index - currentItem, SCROLLING_DURATION);  
  336.             } else {  
  337.                 invalidateLayouts();  
  338.               
  339.                 int old = currentItem;  
  340.                 currentItem = index;  
  341.               
  342.                 notifyChangingListeners(old, currentItem);  
  343.               
  344.                 invalidate();  
  345.             }  
  346.         }  
  347.     }  
  348.   
  349.     /** 
  350.      * Sets the current item w/o animation. Does nothing when index is wrong. 
  351.      *  
  352.      * @param index the item index 
  353.      */  
  354.     public void setCurrentItem(int index) {  
  355.         setCurrentItem(index, false);  
  356.     }     
  357.       
  358.     /** 
  359.      * Tests if wheel is cyclic. That means before the 1st item there is shown the last one 
  360.      * @return true if wheel is cyclic 
  361.      */  
  362.     public boolean isCyclic() {  
  363.         return isCyclic;  
  364.     }  
  365.   
  366.     /** 
  367.      * Set wheel cyclic flag 
  368.      * @param isCyclic the flag to set 
  369.      */  
  370.     public void setCyclic(boolean isCyclic) {  
  371.         this.isCyclic = isCyclic;  
  372.           
  373.         invalidate();  
  374.         invalidateLayouts();  
  375.     }  
  376.   
  377.     /** 
  378.      * Invalidates layouts 
  379.      */  
  380.     private void invalidateLayouts() {  
  381.         itemsLayout = null;  
  382.         valueLayout = null;  
  383.         scrollingOffset = 0;  
  384.     }  
  385.   
  386.     /** 
  387.      * Initializes resources 
  388.      */  
  389.     private void initResourcesIfNecessary() {  
  390.         if (itemsPaint == null) {  
  391.             itemsPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG  
  392.                     | Paint.FAKE_BOLD_TEXT_FLAG);  
  393.             //itemsPaint.density = getResources().getDisplayMetrics().density;   
  394.             itemsPaint.setTextSize(TEXT_SIZE);  
  395.         }  
  396.   
  397.         if (valuePaint == null) {  
  398.             valuePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG  
  399.                     | Paint.FAKE_BOLD_TEXT_FLAG | Paint.DITHER_FLAG);  
  400.             //valuePaint.density = getResources().getDisplayMetrics().density;   
  401.             valuePaint.setTextSize(TEXT_SIZE);  
  402.             valuePaint.setShadowLayer(0.1f, 00.1f, 0xFFC0C0C0);  
  403.         }  
  404.   
  405.         if (centerDrawable == null) {  
  406.             centerDrawable = getContext().getResources().getDrawable(R.drawable.wheel_val);  
  407.         }  
  408.   
  409.         if (topShadow == null) {  
  410.             topShadow = new GradientDrawable(Orientation.TOP_BOTTOM, SHADOWS_COLORS);  
  411.         }  
  412.   
  413.         if (bottomShadow == null) {  
  414.             bottomShadow = new GradientDrawable(Orientation.BOTTOM_TOP, SHADOWS_COLORS);  
  415.         }  
  416.   
  417.         setBackgroundResource(R.drawable.wheel_bg);  
  418.     }  
  419.   
  420.     /** 
  421.      * Calculates desired height for layout 
  422.      *  
  423.      * @param layout 
  424.      *            the source layout 
  425.      * @return the desired layout height 
  426.      */  
  427.     private int getDesiredHeight(Layout layout) {  
  428.         if (layout == null) {  
  429.             return 0;  
  430.         }  
  431.   
  432.         int desired = getItemHeight() * visibleItems - ITEM_OFFSET * 2  
  433.                 - ADDITIONAL_ITEM_HEIGHT;  
  434.   
  435.         // Check against our minimum height   
  436.         desired = Math.max(desired, getSuggestedMinimumHeight());  
  437.   
  438.         return desired;  
  439.     }  
  440.   
  441.     /** 
  442.      * Returns text item by index 
  443.      * @param index the item index 
  444.      * @return the item or null 
  445.      */  
  446.     private String getTextItem(int index) {  
  447.         if (adapter == null || adapter.getItemsCount() == 0) {  
  448.             return null;  
  449.         }  
  450.         int count = adapter.getItemsCount();  
  451.         if ((index < 0 || index >= count) && !isCyclic) {  
  452.             return null;  
  453.         } else {  
  454.             while (index < 0) {  
  455.                 index = count + index;  
  456.             }  
  457.         }  
  458.           
  459.         index %= count;  
  460.         return adapter.getItem(index);  
  461.     }  
  462.       
  463.     /** 
  464.      * Builds text depending on current value 
  465.      *  
  466.      * @param useCurrentValue 
  467.      * @return the text 
  468.      */  
  469.     private String buildText(boolean useCurrentValue) {  
  470.         StringBuilder itemsText = new StringBuilder();  
  471.         int addItems = visibleItems / 2 + 1;  
  472.   
  473.         for (int i = currentItem - addItems; i <= currentItem + addItems; i++) {  
  474.             if (useCurrentValue || i != currentItem) {  
  475.                 String text = getTextItem(i);  
  476.                 if (text != null) {  
  477.                     itemsText.append(text);  
  478.                 }  
  479.             }  
  480.             if (i < currentItem + addItems) {  
  481.                 itemsText.append("\n");  
  482.             }  
  483.         }  
  484.           
  485.         return itemsText.toString();  
  486.     }  
  487.   
  488.     /** 
  489.      * Returns the max item length that can be present 
  490.      * @return the max length 
  491.      */  
  492.     private int getMaxTextLength() {  
  493.         WheelAdapter adapter = getAdapter();  
  494.         if (adapter == null) {  
  495.             return 0;  
  496.         }  
  497.           
  498.         int adapterLength = adapter.getMaximumLength();  
  499.         if (adapterLength > 0) {  
  500.             return adapterLength;  
  501.         }  
  502.   
  503.         String maxText = null;  
  504.         int addItems = visibleItems / 2;  
  505.         for (int i = Math.max(currentItem - addItems, 0);  
  506.                 i < Math.min(currentItem + visibleItems, adapter.getItemsCount()); i++) {  
  507.             String text = adapter.getItem(i);  
  508.             if (text != null && (maxText == null || maxText.length() < text.length())) {  
  509.                 maxText = text;  
  510.             }  
  511.         }  
  512.   
  513.         return maxText != null ? maxText.length() : 0;  
  514.     }  
  515.   
  516.     /** 
  517.      * Returns height of wheel item 
  518.      * @return the item height 
  519.      */  
  520.     private int getItemHeight() {  
  521.         if (itemHeight != 0) {  
  522.             return itemHeight;  
  523.         } else if (itemsLayout != null && itemsLayout.getLineCount() > 2) {  
  524.             itemHeight = itemsLayout.getLineTop(2) - itemsLayout.getLineTop(1);  
  525.             return itemHeight;  
  526.         }  
  527.           
  528.         return getHeight() / visibleItems;  
  529.     }  
  530.   
  531.     /** 
  532.      * Calculates control width and creates text layouts 
  533.      * @param widthSize the input layout width 
  534.      * @param mode the layout mode 
  535.      * @return the calculated control width 
  536.      */  
  537.     private int calculateLayoutWidth(int widthSize, int mode) {  
  538.         initResourcesIfNecessary();  
  539.   
  540.         int width = widthSize;  
  541.   
  542.         int maxLength = getMaxTextLength();  
  543.         if (maxLength > 0) {  
  544.             float textWidth = FloatMath.ceil(Layout.getDesiredWidth("0", itemsPaint));  
  545.             itemsWidth = (int) (maxLength * textWidth);  
  546.         } else {  
  547.             itemsWidth = 0;  
  548.         }  
  549.         itemsWidth += ADDITIONAL_ITEMS_SPACE; // make it some more   
  550.   
  551.         labelWidth = 0;  
  552.         if (label != null && label.length() > 0) {  
  553.             labelWidth = (int) FloatMath.ceil(Layout.getDesiredWidth(label, valuePaint));  
  554.         }  
  555.   
  556.         boolean recalculate = false;  
  557.         if (mode == MeasureSpec.EXACTLY) {  
  558.             width = widthSize;  
  559.             recalculate = true;  
  560.         } else {  
  561.             width = itemsWidth + labelWidth + 2 * PADDING;  
  562.             if (labelWidth > 0) {  
  563.                 width += LABEL_OFFSET;  
  564.             }  
  565.   
  566.             // Check against our minimum width   
  567.             width = Math.max(width, getSuggestedMinimumWidth());  
  568.   
  569.             if (mode == MeasureSpec.AT_MOST && widthSize < width) {  
  570.                 width = widthSize;  
  571.                 recalculate = true;  
  572.             }  
  573.         }  
  574.   
  575.         if (recalculate) {  
  576.             // recalculate width   
  577.             int pureWidth = width - LABEL_OFFSET - 2 * PADDING;  
  578.             if (pureWidth <= 0) {  
  579.                 itemsWidth = labelWidth = 0;  
  580.             }  
  581.             if (labelWidth > 0) {  
  582.                 double newWidthItems = (double) itemsWidth * pureWidth  
  583.                         / (itemsWidth + labelWidth);  
  584.                 itemsWidth = (int) newWidthItems;  
  585.                 labelWidth = pureWidth - itemsWidth;  
  586.             } else {  
  587.                 itemsWidth = pureWidth + LABEL_OFFSET; // no label   
  588.             }  
  589.         }  
  590.   
  591.         if (itemsWidth > 0) {  
  592.             createLayouts(itemsWidth, labelWidth);  
  593.         }  
  594.   
  595.         return width;  
  596.     }  
  597.   
  598.     /** 
  599.      * Creates layouts 
  600.      * @param widthItems width of items layout 
  601.      * @param widthLabel width of label layout 
  602.      */  
  603.     private void createLayouts(int widthItems, int widthLabel) {  
  604.         if (itemsLayout == null || itemsLayout.getWidth() > widthItems) {  
  605.             itemsLayout = new StaticLayout(buildText(isScrollingPerformed), itemsPaint, widthItems,  
  606.                     widthLabel > 0 ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_CENTER,  
  607.                     1, ADDITIONAL_ITEM_HEIGHT, false);  
  608.         } else {  
  609.             itemsLayout.increaseWidthTo(widthItems);  
  610.         }  
  611.   
  612.         if (!isScrollingPerformed && (valueLayout == null || valueLayout.getWidth() > widthItems)) {  
  613.             String text = getAdapter() != null ? getAdapter().getItem(currentItem) : null;  
  614.             valueLayout = new StaticLayout(text != null ? text : "",  
  615.                     valuePaint, widthItems, widthLabel > 0 ?  
  616.                             Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_CENTER,  
  617.                             1, ADDITIONAL_ITEM_HEIGHT, false);  
  618.         } else if (isScrollingPerformed) {  
  619.             valueLayout = null;  
  620.         } else {  
  621.             valueLayout.increaseWidthTo(widthItems);  
  622.         }  
  623.   
  624.         if (widthLabel > 0) {  
  625.             if (labelLayout == null || labelLayout.getWidth() > widthLabel) {  
  626.                 labelLayout = new StaticLayout(label, valuePaint,  
  627.                         widthLabel, Layout.Alignment.ALIGN_NORMAL, 1,  
  628.                         ADDITIONAL_ITEM_HEIGHT, false);  
  629.             } else {  
  630.                 labelLayout.increaseWidthTo(widthLabel);  
  631.             }  
  632.         }  
  633.     }  
  634.   
  635.     @Override  
  636.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  637.         int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  638.         int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  639.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  640.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  641.   
  642.         int width = calculateLayoutWidth(widthSize, widthMode);  
  643.   
  644.         int height;  
  645.         if (heightMode == MeasureSpec.EXACTLY) {  
  646.             height = heightSize;  
  647.         } else {  
  648.             height = getDesiredHeight(itemsLayout);  
  649.   
  650.             if (heightMode == MeasureSpec.AT_MOST) {  
  651.                 height = Math.min(height, heightSize);  
  652.             }  
  653.         }  
  654.   
  655.         setMeasuredDimension(width, height);  
  656.     }  
  657.   
  658.     @Override  
  659.     protected void onDraw(Canvas canvas) {  
  660.         super.onDraw(canvas);  
  661.           
  662.         if (itemsLayout == null) {  
  663.             if (itemsWidth == 0) {  
  664.                 calculateLayoutWidth(getWidth(), MeasureSpec.EXACTLY);  
  665.             } else {  
  666.                 createLayouts(itemsWidth, labelWidth);  
  667.             }  
  668.         }  
  669.   
  670.         if (itemsWidth > 0) {  
  671.             canvas.save();  
  672.             // Skip padding space and hide a part of top and bottom items   
  673.             canvas.translate(PADDING, -ITEM_OFFSET);  
  674.             drawItems(canvas);  
  675.             drawValue(canvas);  
  676.             canvas.restore();  
  677.         }  
  678.   
  679.         drawCenterRect(canvas);  
  680.         drawShadows(canvas);  
  681.     }  
  682.   
  683.     /** 
  684.      * Draws shadows on top and bottom of control 
  685.      * @param canvas the canvas for drawing 
  686.      */  
  687.     private void drawShadows(Canvas canvas) {  
  688.         topShadow.setBounds(00, getWidth(), getHeight() / visibleItems);  
  689.         topShadow.draw(canvas);  
  690.   
  691.         bottomShadow.setBounds(0, getHeight() - getHeight() / visibleItems,  
  692.                 getWidth(), getHeight());  
  693.         bottomShadow.draw(canvas);  
  694.     }  
  695.   
  696.     /** 
  697.      * Draws value and label layout 
  698.      * @param canvas the canvas for drawing 
  699.      */  
  700.     private void drawValue(Canvas canvas) {  
  701.         valuePaint.setColor(VALUE_TEXT_COLOR);  
  702.         valuePaint.drawableState = getDrawableState();  
  703.   
  704.         Rect bounds = new Rect();  
  705.         itemsLayout.getLineBounds(visibleItems / 2, bounds);  
  706.   
  707.         // draw label   
  708.         if (labelLayout != null) {  
  709.             canvas.save();  
  710.             canvas.translate(itemsLayout.getWidth() + LABEL_OFFSET, bounds.top);  
  711.             labelLayout.draw(canvas);  
  712.             canvas.restore();  
  713.         }  
  714.   
  715.         // draw current value   
  716.         if (valueLayout != null) {  
  717.             canvas.save();  
  718.             canvas.translate(0, bounds.top + scrollingOffset);  
  719.             valueLayout.draw(canvas);  
  720.             canvas.restore();  
  721.         }  
  722.     }  
  723.   
  724.     /** 
  725.      * Draws items 
  726.      * @param canvas the canvas for drawing 
  727.      */  
  728.     private void drawItems(Canvas canvas) {  
  729.         canvas.save();  
  730.           
  731.         int top = itemsLayout.getLineTop(1);  
  732.         canvas.translate(0, - top + scrollingOffset);  
  733.           
  734.         itemsPaint.setColor(ITEMS_TEXT_COLOR);  
  735.         itemsPaint.drawableState = getDrawableState();  
  736.         itemsLayout.draw(canvas);  
  737.           
  738.         canvas.restore();  
  739.     }  
  740.   
  741.     /** 
  742.      * Draws rect for current value 
  743.      * @param canvas the canvas for drawing 
  744.      */  
  745.     private void drawCenterRect(Canvas canvas) {  
  746.         int center = getHeight() / 2;  
  747.         int offset = getItemHeight() / 2;  
  748.         centerDrawable.setBounds(0, center - offset, getWidth(), center + offset);  
  749.         centerDrawable.draw(canvas);  
  750.     }  
  751.   
  752.     @Override  
  753.     public boolean onTouchEvent(MotionEvent event) {  
  754.         WheelAdapter adapter = getAdapter();  
  755.         if (adapter == null) {  
  756.             return true;  
  757.         }  
  758.           
  759.             if (!gestureDetector.onTouchEvent(event) && event.getAction() == MotionEvent.ACTION_UP) {  
  760.             justify();  
  761.         }  
  762.         return true;  
  763.     }  
  764.       
  765.     /** 
  766.      * Scrolls the wheel 
  767.      * @param delta the scrolling value 
  768.      */  
  769.     private void doScroll(int delta) {  
  770.         scrollingOffset += delta;  
  771.           
  772.         int count = scrollingOffset / getItemHeight();  
  773.         int pos = currentItem - count;  
  774.         if (isCyclic && adapter.getItemsCount() > 0) {  
  775.             // fix position by rotating   
  776.             while (pos < 0) {  
  777.                 pos += adapter.getItemsCount();  
  778.             }  
  779.             pos %= adapter.getItemsCount();  
  780.         } else if (isScrollingPerformed) {  
  781.             //    
  782.             if (pos < 0) {  
  783.                 count = currentItem;  
  784.                 pos = 0;  
  785.             } else if (pos >= adapter.getItemsCount()) {  
  786.                 count = currentItem - adapter.getItemsCount() + 1;  
  787.                 pos = adapter.getItemsCount() - 1;  
  788.             }  
  789.         } else {  
  790.             // fix position   
  791.             pos = Math.max(pos, 0);  
  792.             pos = Math.min(pos, adapter.getItemsCount() - 1);  
  793.         }  
  794.           
  795.         int offset = scrollingOffset;  
  796.         if (pos != currentItem) {  
  797.             setCurrentItem(pos, false);  
  798.         } else {  
  799.             invalidate();  
  800.         }  
  801.           
  802.         // update offset   
  803.         scrollingOffset = offset - count * getItemHeight();  
  804.         if (scrollingOffset > getHeight()) {  
  805.             scrollingOffset = scrollingOffset % getHeight() + getHeight();  
  806.         }  
  807.     }  
  808.       
  809.     // gesture listener   
  810.     private SimpleOnGestureListener gestureListener = new SimpleOnGestureListener() {  
  811.         public boolean onDown(MotionEvent e) {  
  812.             if (isScrollingPerformed) {  
  813.                 scroller.forceFinished(true);  
  814.                 clearMessages();  
  815.                 return true;  
  816.             }  
  817.             return false;  
  818.         }  
  819.           
  820.         public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {  
  821.             startScrolling();  
  822.             doScroll((int)-distanceY);  
  823.             return true;  
  824.         }  
  825.           
  826.         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {  
  827.             lastScrollY = currentItem * getItemHeight() + scrollingOffset;  
  828.             int maxY = isCyclic ? 0x7FFFFFFF : adapter.getItemsCount() * getItemHeight();  
  829.             int minY = isCyclic ? -maxY : 0;  
  830.             scroller.fling(0, lastScrollY, 0, (int) -velocityY / 200, minY, maxY);  
  831.             setNextMessage(MESSAGE_SCROLL);  
  832.             return true;  
  833.         }  
  834.     };  
  835.   
  836.     // Messages   
  837.     private final int MESSAGE_SCROLL = 0;  
  838.     private final int MESSAGE_JUSTIFY = 1;  
  839.       
  840.     /** 
  841.      * Set next message to queue. Clears queue before. 
  842.      *  
  843.      * @param message the message to set 
  844.      */  
  845.     private void setNextMessage(int message) {  
  846.         clearMessages();  
  847.         animationHandler.sendEmptyMessage(message);  
  848.     }  
  849.   
  850.     /** 
  851.      * Clears messages from queue 
  852.      */  
  853.     private void clearMessages() {  
  854.         animationHandler.removeMessages(MESSAGE_SCROLL);  
  855.         animationHandler.removeMessages(MESSAGE_JUSTIFY);  
  856.     }  
  857.       
  858.     // animation handler   
  859.     private Handler animationHandler = new Handler() {  
  860.         public void handleMessage(Message msg) {  
  861.             scroller.computeScrollOffset();  
  862.             int currY = scroller.getCurrY();  
  863.             int delta = lastScrollY - currY;  
  864.             lastScrollY = currY;  
  865.             if (delta != 0) {  
  866.                 doScroll(delta);  
  867.             }  
  868.               
  869.             // scrolling is not finished when it comes to final Y   
  870.             // so, finish it manually    
  871.             if (Math.abs(currY - scroller.getFinalY()) < MIN_DELTA_FOR_SCROLLING) {  
  872.                 currY = scroller.getFinalY();  
  873.                 scroller.forceFinished(true);  
  874.             }  
  875.             if (!scroller.isFinished()) {  
  876.                 animationHandler.sendEmptyMessage(msg.what);  
  877.             } else if (msg.what == MESSAGE_SCROLL) {  
  878.                 justify();  
  879.             } else {  
  880.                 finishScrolling();  
  881.             }  
  882.         }  
  883.     };  
  884.       
  885.     /** 
  886.      * Justifies wheel 
  887.      */  
  888.     private void justify() {  
  889.         if (adapter == null) {  
  890.             return;  
  891.         }  
  892.           
  893.         lastScrollY = 0;  
  894.         int offset = scrollingOffset;  
  895.         int itemHeight = getItemHeight();  
  896.         boolean needToIncrease = offset > 0 ? currentItem < adapter.getItemsCount() : currentItem > 0;   
  897.         if ((isCyclic || needToIncrease) && Math.abs((float) offset) > (float) itemHeight / 2) {  
  898.             if (offset < 0)  
  899.                 offset += itemHeight + MIN_DELTA_FOR_SCROLLING;  
  900.             else  
  901.                 offset -= itemHeight + MIN_DELTA_FOR_SCROLLING;  
  902.         }  
  903.         if (Math.abs(offset) > MIN_DELTA_FOR_SCROLLING) {  
  904.             scroller.startScroll(000, offset, SCROLLING_DURATION);  
  905.             setNextMessage(MESSAGE_JUSTIFY);  
  906.         } else {  
  907.             finishScrolling();  
  908.         }  
  909.     }  
  910.       
  911.     /** 
  912.      * Starts scrolling 
  913.      */  
  914.     private void startScrolling() {  
  915.         if (!isScrollingPerformed) {  
  916.             isScrollingPerformed = true;  
  917.             notifyScrollingListenersAboutStart();  
  918.         }  
  919.     }  
  920.   
  921.     /** 
  922.      * Finishes scrolling 
  923.      */  
  924.     void finishScrolling() {  
  925.         if (isScrollingPerformed) {  
  926.             notifyScrollingListenersAboutEnd();  
  927.             isScrollingPerformed = false;  
  928.         }  
  929.         invalidateLayouts();  
  930.         invalidate();  
  931.     }  
  932.       
  933.       
  934.     /** 
  935.      * Scroll the wheel 
  936.      * @param itemsToSkip items to scroll 
  937.      * @param time scrolling duration 
  938.      */  
  939.     public void scroll(int itemsToScroll, int time) {  
  940.         scroller.forceFinished(true);  
  941.   
  942.         lastScrollY = scrollingOffset;  
  943.         int offset = itemsToScroll * getItemHeight();  
  944.           
  945.         scroller.startScroll(0, lastScrollY, 0, offset - lastScrollY, time);  
  946.         setNextMessage(MESSAGE_SCROLL);  
  947.           
  948.         startScrolling();  
  949.     }  
  950.   
  951. }  
  • 1
  • 2
  • 下一页

相关内容