Android中WheelView代碼分析筆記1(明天繼續分析 >>>>>)

package com.guozg.wheelview.views;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.GradientDrawable.Orientation;
import android.os.Handler;
import android.os.Message;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
import android.widget.Scroller;

import com.guozg.wheelview.R;

import java.util.LinkedList;
import java.util.List;

/**
 * Numeric wheel view.
 */
public class WheelView extends View {
    /**
     * Scrolling duration
     */
    private static final int SCROLLING_DURATION = 400;

    /**
     * Minimum delta for scrolling
     */
    private static final int MIN_DELTA_FOR_SCROLLING = 1; //判斷是否需要滾動調整的誤差值

    /**
     * 選中項的顏色值
     */
    private static final int VALUE_TEXT_COLOR = 0xFFFF0000;

    /**
     * 未選中項的顏色值
     */
    private static final int ITEMS_TEXT_COLOR = 0xFF000000;

    /**
     * 頂部和底部的陰影
     */
    private static final int[] SHADOWS_COLORS = new int[]{0xFF111111, 0x00AAAAAA, 0x00AAAAAA};

    /**
     * 額外的列表項 高度 是加到標準的列表項高度上去的
     */
    private static final int ADDITIONAL_ITEM_HEIGHT = 15;

    /**
     * Text size
     */
    private static final int TEXT_SIZE = 24;

    /**
     * 頂部和底部的偏移量 用來隱藏
     */
    private static final int ITEM_OFFSET = TEXT_SIZE / 5;

    /**
     * item 額外添加的高度
     */
    private static final int ADDITIONAL_ITEMS_SPACE = 10;

    /**
     * Label offset
     */
    private static final int LABEL_OFFSET = 8;

    /**
     * 左右兩邊的padding值
     */
    private static final int PADDING = 10;

    /**
     * 默認的可見item個數
     */
    private static final int DEF_VISIBLE_ITEMS = 5;
    // Messages
    private final int MESSAGE_SCROLL = 0; //滾動
    private final int MESSAGE_JUSTIFY = 1; //調整
    // Cyclic
    boolean isCyclic = false;
    // Wheel Values
    private WheelAdapter adapter = null;
    private int currentItem = 0;
    // Widths
    private int itemsWidth = 0;
    private int labelWidth = 0;
    // Count of visible items
    private int visibleItems = DEF_VISIBLE_ITEMS;

    //StaticLayout 解析 : 該組件用於顯示文本,
    // 一旦該文本被顯示後, 就不能再編輯, 如果想要修改文本, 使用 DynamicLayout 佈局即可;
    // Item height  item的高度
    private int itemHeight = 0;
    // Text paints
    private TextPaint itemsPaint; //普通文字畫筆
    private TextPaint valuePaint;//選中文字的畫筆  >>>>肯定是字體顏色突出一點咯 字體大一點什麼的
    //使用場景 : 一般情況下不會使用該組件, 當想要自定義組件 或者 想要使用 Canvas 繪製文本時 才使用該佈局;
    private StaticLayout itemsLayout; //普通條目佈局
    private StaticLayout labelLayout;//選中條目佈局
    private StaticLayout valueLayout;//標籤佈局  >>>>沒看作用 目前
    // Label & background
    private String label;
    private Drawable centerDrawable;  //選中條的背景
    // //漸變的Drawable 我們經常在 drawable是用的shape中gradient的屬性 >>相對應
    private GradientDrawable topShadow; //頭部的 >>這兩個應該是相反
    private GradientDrawable bottomShadow;//底部的
    // Scrolling
    private boolean isScrollingPerformed; //是否正在執行 滾動 動作
    private int scrollingOffset; //滾動中的偏移量
    // Scrolling animation
    private GestureDetector gestureDetector; //手勢
    private Scroller scroller; //用於 平滑滾動的
    private int lastScrollY;
    // Listeners >>>>>>>>>>>>>>>>>>  可以設置多個監聽器
    private List<OnWheelChangedListener> changingListeners = new LinkedList<OnWheelChangedListener>();
    private List<OnWheelScrollListener> scrollingListeners = new LinkedList<OnWheelScrollListener>();
    // animation handler
    private Handler animationHandler = new Handler() {
        public void handleMessage(Message msg) {
            scroller.computeScrollOffset(); //返回值爲boolean,true說明滾動尚未完成,
            // false說明滾動已經完成。這是一個很重要的方法,通常放在View.computeScroll()中,用來判斷是否滾動是否結束。
            //

            int currY = scroller.getCurrY();
            int delta = lastScrollY - currY;
            lastScrollY = currY;
            if (delta != 0) {
                doScroll(delta);
            }

            //當滾動到對應的位置的時候 ,Scroller的狀態還沒有改變 所以需要我們去手動的結束 最後的滾動
            if (Math.abs(currY - scroller.getFinalY()) < MIN_DELTA_FOR_SCROLLING) {
                currY = scroller.getFinalY();
                scroller.forceFinished(true);
            }
            if (!scroller.isFinished()) { //如果還是沒有停止 繼續請求 >>>
                animationHandler.sendEmptyMessage(msg.what); //>>>繼續回調 >>>完成動畫
            } else if (msg.what == MESSAGE_SCROLL) {
                justify(); //如果 動畫已經停止 就 微調一下 
            } else {
                finishScrolling();
            }
        }
    };
    // gesture listener
    private SimpleOnGestureListener gestureListener = new SimpleOnGestureListener() {
        public boolean onDown(MotionEvent e) {
            if (isScrollingPerformed) {
                scroller.forceFinished(true);
                clearMessages();
                return true;
            }
            return false;
        }

        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            startScrolling();
            doScroll((int) -distanceY);
            return true;
        }

        /**
         *
         * @param e1
         * @param e2
         * @param velocityX
         * @param velocityY
         * @return
         */
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            lastScrollY = currentItem * getItemHeight() + scrollingOffset;
            int maxY = isCyclic ? 0x7FFFFFFF : adapter.getItemsCount() * getItemHeight();
            int minY = isCyclic ? -maxY : 0;
            scroller.fling(0, lastScrollY, 0, (int) -velocityY / 2, 0, 0, minY, maxY);
            setNextMessage(MESSAGE_SCROLL);
            return true;
        }
    };

    /**
     * Constructor
     */
    public WheelView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initData(context);
    }

    /**
     * Constructor
     */
    public WheelView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initData(context);
    }

    /**
     * Constructor
     */
    public WheelView(Context context) {
        super(context);
        initData(context);
    }

    /**
     * Initializes class data
     *
     * @param context the context
     */
    private void initData(Context context) {
        gestureDetector = new GestureDetector(context, gestureListener); //初始化手勢監聽器
        gestureDetector.setIsLongpressEnabled(false); //長按

        scroller = new Scroller(context); //初始化 滑動
    }

    /**
     * Gets wheel adapter
     *
     * @return the adapter
     */
    public WheelAdapter getAdapter() {
        return adapter;
    }

    /**
     * Sets wheel adapter
     *
     * @param adapter the new wheel adapter
     */
    public void setAdapter(WheelAdapter adapter) {
        this.adapter = adapter;
        invalidateLayouts(); //刷新佈局
        invalidate(); //重繪
    }

    /**
     * Set the the specified scrolling interpolator
     * <p/>
     * 設置指定的滾動插值器
     *
     * @param interpolator the interpolator
     */
    public void setInterpolator(Interpolator interpolator) {
        scroller.forceFinished(true); //強制關閉當前的滾動  新創建一個有 插值器的
        scroller = new Scroller(getContext(), interpolator);
    }

    /**
     * Gets count of visible items
     *
     * @return the count of visible items
     */
    public int getVisibleItems() {
        return visibleItems;
    }

    /**
     * Sets count of visible items
     *
     * @param count the new count
     */
    public void setVisibleItems(int count) {
        visibleItems = count;
        invalidate();
    }

    /**
     * Gets label
     *
     * @return the label
     */
    public String getLabel() {
        return label;
    }

    /**
     * Sets label
     *
     * @param newLabel the label to set
     */
    public void setLabel(String newLabel) {
        if (label == null || !label.equals(newLabel)) {
            label = newLabel;
            labelLayout = null;
            invalidate();
        }
    }

    /**
     * Adds wheel changing listener
     *
     * @param listener the listener
     */
    public void addChangingListener(OnWheelChangedListener listener) {
        changingListeners.add(listener);
    }

    /**
     * Removes wheel changing listener
     *
     * @param listener the listener
     */
    public void removeChangingListener(OnWheelChangedListener listener) {
        changingListeners.remove(listener);
    }

    /**
     * Notifies changing listeners
     *
     * @param oldValue the old wheel value
     * @param newValue the new wheel value
     */
    protected void notifyChangingListeners(int oldValue, int newValue) {
        for (OnWheelChangedListener listener : changingListeners) {
            listener.onChanged(this, oldValue, newValue); //去通知 數據 改變
        }
    }

    /**
     * Adds wheel scrolling listener
     *
     * @param listener the listener
     */
    public void addScrollingListener(OnWheelScrollListener listener) {
        scrollingListeners.add(listener);
    }

    /**
     * Removes wheel scrolling listener
     *
     * @param listener the listener
     */
    public void removeScrollingListener(OnWheelScrollListener listener) {
        scrollingListeners.remove(listener);
    }

    /**
     * Notifies listeners about starting scrolling
     */
    protected void notifyScrollingListenersAboutStart() {
        for (OnWheelScrollListener listener : scrollingListeners) {
            listener.onScrollingStarted(this);
        }
    }

    /**
     * Notifies listeners about ending scrolling
     */
    protected void notifyScrollingListenersAboutEnd() {
        for (OnWheelScrollListener listener : scrollingListeners) {
            listener.onScrollingFinished(this); //通知滾動事件
        }
    }

    /**
     * Gets current value
     *
     * @return the current value
     */
    public int getCurrentItem() {
        return currentItem;
    }

    /**
     * Sets the current item w/o animation. Does nothing when index is wrong.
     *
     * @param index the item index
     */
    public void setCurrentItem(int index) {
        setCurrentItem(index, false);
    }

    /**
     * 設置當前選中項 如果 下標異常 直接不處理 如果下標 正常
     * 判斷是否需要動畫效果
     * 如果 需要 就使用scroller平滑的滑動過去
     * 如果 不需要就使界面失效 直接重繪 界面
     *
     * @param index    需要設置的下標
     * @param animated 是否需要動畫的標誌
     */
    public void setCurrentItem(int index, boolean animated) {
        if (adapter == null || adapter.getItemsCount() == 0) {
            return; // throw?
        }
        if (index < 0 || index >= adapter.getItemsCount()) {
            if (isCyclic) {  //如果 是循環 那麼 就不算 越界  做個 判斷 就能求出來 位置
                while (index < 0) {
                    index += adapter.getItemsCount();
                }
                index %= adapter.getItemsCount();
            } else { //如果是不能循環 那就是錯誤的下標
                return; // throw?
            }
        }
        if (index != currentItem) {  //如果 設置 不是 當前 一致  就需要跳轉 到  友好界面 滑過去
            if (animated) {  //是否是有動畫效果 有 就滑過去
                scroll(index - currentItem, SCROLLING_DURATION);
            } else {
                //沒有 直接使得界面失效 得了
                invalidateLayouts();

                //順便 通知一下 改變了 數據哦  把 就數據 和新數據的下標 傳遞過去
                int old = currentItem;
                currentItem = index;

                notifyChangingListeners(old, currentItem);

                invalidate(); //直接重繪
            }
        }
    }

    /**
     * Tests if wheel is cyclic. That means before the 1st item there is shown
     * the last one
     *
     * @return true if wheel is cyclic
     */
    public boolean isCyclic() {
        return isCyclic;
    }

    /**
     * Set wheel cyclic flag
     *
     * @param isCyclic the flag to set
     */
    public void setCyclic(boolean isCyclic) {
        this.isCyclic = isCyclic;

        invalidate();
        invalidateLayouts();
    }

    /**
     * Invalidates layouts
     */
    private void invalidateLayouts() {
        itemsLayout = null;
        valueLayout = null;
        scrollingOffset = 0;
    }

    /**
     * Initializes resources
     */
    private void initResourcesIfNecessary() {
        if (itemsPaint == null) { //普通項的畫筆
            itemsPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.FAKE_BOLD_TEXT_FLAG);
            // itemsPaint.density = getResources().getDisplayMetrics().density;
            itemsPaint.setTextSize(TEXT_SIZE);
        }

        if (valuePaint == null) { //選中項的畫筆
            valuePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.FAKE_BOLD_TEXT_FLAG | Paint.DITHER_FLAG);
            // valuePaint.density = getResources().getDisplayMetrics().density;
            valuePaint.setTextSize(TEXT_SIZE);
            valuePaint.setShadowLayer(0.1f, 0, 0.1f, 0xFFC0C0C0);
        }

        //獲取列表重心的 背景
        if (centerDrawable == null) {
            centerDrawable = getContext().getResources().getDrawable(R.drawable.wheel_val);
        }

        //恰好相反 >>>>爲了 突出中心  中間 一個顏色 上下一一致
        if (topShadow == null) {
            topShadow = new GradientDrawable(Orientation.TOP_BOTTOM, SHADOWS_COLORS);
        }

        if (bottomShadow == null) {
            bottomShadow = new GradientDrawable(Orientation.BOTTOM_TOP, SHADOWS_COLORS);
        }

        setBackgroundResource(R.drawable.wheel_bg);
    }

    /**
     * 計算出 佈局想要的高度
     *
     * @param layout 傳入的是想要 進行計算的佈局
     * @return 返回該佈局想要的高度
     */
    private int getDesiredHeight(Layout layout) {
        if (layout == null) {
            return 0;
        }

        int desired = getItemHeight() * visibleItems - ITEM_OFFSET * 2 - ADDITIONAL_ITEM_HEIGHT;

        // Check against our minimum height
        desired = Math.max(desired, getSuggestedMinimumHeight());

        return desired;
    }

    /**
     * 根據下標返回文本數據
     *
     * @param index the item index
     * @return the item or null
     */
    private String getTextItem(int index) {
        if (adapter == null || adapter.getItemsCount() == 0) {
            return null;
        }
        int count = adapter.getItemsCount();
        if ((index < 0 || index >= count) && !isCyclic) {
            return null;
        } else {
            while (index < 0) {
                index = count + index;
            }
        }

        index %= count;
        return adapter.getItem(index);
    }

    /**
     * Builds text depending on current value
     *
     * @param useCurrentValue
     * @return the text
     */
    private String buildText(boolean useCurrentValue) {
        StringBuilder itemsText = new StringBuilder();
        int addItems = visibleItems / 2 + 1;

        for (int i = currentItem - addItems; i <= currentItem + addItems; i++) { //獲取 當前屏幕的所有顯示的文本
            if (useCurrentValue || i != currentItem) { //正在 滾動 或者 不是當前選中的View的時候 >>>>.這裏已經過濾掉了選中狀態下的當前Item的相應文本
                String text = getTextItem(i);
                if (text != null) {
                    itemsText.append(text);
                }
            }
            if (i < currentItem + addItems) {
                itemsText.append("\n");
            }
        }

        return itemsText.toString();
    }

    /**
     * 返回 最大的文本 寬度 是的 可以完整的呈現出來
     *
     * @return the max length
     */
    private int getMaxTextLength() {
        WheelAdapter adapter = getAdapter();
        if (adapter == null) {
            return 0;
        }

        int adapterLength = adapter.getMaximumLength();
        if (adapterLength > 0) { //如果是已經重寫了該方法的適配器 應該是可以獲取的
            return adapterLength;
        }
        //下面是針對 沒有 重寫適配器中的該方法的情況
        String maxText = null;
        int addItems = visibleItems / 2; //求出 課件項的重點
        //對所有 可見item 進行 便利 取出 最長字符串
        for (int i = Math.max(currentItem - addItems, 0);  //第一個可見
             i < Math.min(currentItem + visibleItems,//最後一個課件
                     adapter.getItemsCount()); i++) { //遍歷
            String text = adapter.getItem(i); //獲取文本
            if (text != null && (maxText == null || maxText.length() < text.length())) {
                maxText = text;
            }
        }

        return maxText != null ? maxText.length() : 0; //返回最大高度
    }

    /**
     * Returns height of wheel item
     *
     * @return the item height
     */
    private int getItemHeight() {
        if (itemHeight != 0) {
            return itemHeight;
        } else if (itemsLayout != null && itemsLayout.getLineCount() > 2) {
            itemHeight = itemsLayout.getLineTop(2) - itemsLayout.getLineTop(1);
            return itemHeight;
        }

        return getHeight() / visibleItems;
    }

    /**
     * Calculates control width and creates text layouts
     *
     * @param widthSize the input layout width
     * @param mode      the layout mode
     * @return the calculated control width
     */
    private int calculateLayoutWidth(int widthSize, int mode) {
        initResourcesIfNecessary();

        int width = widthSize;

        int maxLength = getMaxTextLength();
        if (maxLength > 0) {
            float textWidth = FloatMath.ceil(Layout.getDesiredWidth("0", itemsPaint));
            itemsWidth = (int) (maxLength * textWidth);
        } else {
            itemsWidth = 0;
        }
        itemsWidth += ADDITIONAL_ITEMS_SPACE; // make it some more

        labelWidth = 0;
        if (label != null && label.length() > 0) {
            labelWidth = (int) FloatMath.ceil(Layout.getDesiredWidth(label, valuePaint));
        }

        boolean recalculate = false;
        if (mode == MeasureSpec.EXACTLY) {
            width = widthSize;
            recalculate = true;
        } else {
            width = itemsWidth + labelWidth + 2 * PADDING;
            if (labelWidth > 0) {
                width += LABEL_OFFSET;
            }

            // Check against our minimum width
            width = Math.max(width, getSuggestedMinimumWidth());

            if (mode == MeasureSpec.AT_MOST && widthSize < width) {
                width = widthSize;
                recalculate = true;
            }
        }

        if (recalculate) {
            // recalculate width
            int pureWidth = width - LABEL_OFFSET - 2 * PADDING;
            if (pureWidth <= 0) {
                itemsWidth = labelWidth = 0;
            }
            if (labelWidth > 0) {
                double newWidthItems = (double) itemsWidth * pureWidth / (itemsWidth + labelWidth);
                itemsWidth = (int) newWidthItems;
                labelWidth = pureWidth - itemsWidth;
            } else {
                itemsWidth = pureWidth + LABEL_OFFSET; // no label
            }
        }

        if (itemsWidth > 0) {
            createLayouts(itemsWidth, labelWidth);
        }

        return width;
    }

    /**
     * 創建佈局
     *
     * @param widthItems width of items layout
     * @param widthLabel width of label layout
     */
    private void createLayouts(int widthItems, int widthLabel) {
        //如果還沒有創建 或者是 當前的寬度 大於 所需要的 那麼就進行重新創建
        if (itemsLayout == null || itemsLayout.getWidth() > widthItems) {
            // 1 顯示文本
            // 2畫筆
            // 3 文本寬度 ,
            // 4對齊方式 ,
            // 5行間距, 1f 代表 1 倍字體高度
            // 6基礎行距上增加多少 , 真實行間距 等於 spacingmult 和 spacingadd 的和
            //7 不知道 false

            //需要指出的是這個layout是默認畫在Canvas的(0,0)點的,
            // 如果需要調整位置只能在draw之前移Canvas的起始座標canvas.translate(x, y);
            itemsLayout = new StaticLayout(buildText(isScrollingPerformed), itemsPaint, widthItems,
                    widthLabel > 0 ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_CENTER, 1,
                    ADDITIONAL_ITEM_HEIGHT, false);
        } else {
            itemsLayout.increaseWidthTo(widthItems);
        }
        //下面畫的選中的Item 同理
        if (!isScrollingPerformed && (valueLayout == null || valueLayout.getWidth() > widthItems)) {//沒有在滾動的時候 纔有會選中項
            String text = getAdapter() != null ? getAdapter().getItem(currentItem) : null;
            valueLayout = new StaticLayout(text != null ? text : "", valuePaint, widthItems,
                    widthLabel > 0 ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_CENTER, 1,
                    ADDITIONAL_ITEM_HEIGHT, false);

        } else if (isScrollingPerformed) { //正在滾動 那麼顯示選中的佈局沒有必要顯示
            valueLayout = null;
        } else {
            valueLayout.increaseWidthTo(widthItems);
        }

        if (widthLabel > 0) {
            if (labelLayout == null || labelLayout.getWidth() > widthLabel) {
                labelLayout = new StaticLayout(label, valuePaint, widthLabel, Layout.Alignment.ALIGN_NORMAL, 1,
                        ADDITIONAL_ITEM_HEIGHT, false);
            } else {
                labelLayout.increaseWidthTo(widthLabel);
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width = calculateLayoutWidth(widthSize, widthMode);

        int height;
        if (heightMode == MeasureSpec.EXACTLY) { //精確模式
            height = heightSize; //直接是傳入的值
        } else {
            height = getDesiredHeight(itemsLayout); //獲取最大的

            if (heightMode == MeasureSpec.AT_MOST) { //傳入最大值 和 傳入值的最小值
                height = Math.min(height, heightSize);
            }
        }

        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (itemsLayout == null) {
            if (itemsWidth == 0) {
                calculateLayoutWidth(getWidth(), MeasureSpec.EXACTLY);
            } else {
                createLayouts(itemsWidth, labelWidth);
            }
        }

        if (itemsWidth > 0) {
            canvas.save();
            // Skip padding space and hide a part of top and bottom items
            canvas.translate(PADDING, -ITEM_OFFSET); //平移畫布
            drawItems(canvas); //繪製 普通Item
            drawValue(canvas); //繪製 選中Item
            canvas.restore(); //把畫布恢復
        }

        drawCenterRect(canvas);
        drawShadows(canvas);
    }

    /**
     * 繪製 頂部和底部的陰影
     *
     * @param canvas the canvas for drawing
     */
    private void drawShadows(Canvas canvas) {
        topShadow.setBounds(0, 0, getWidth(), getHeight() / visibleItems);
        topShadow.draw(canvas);

        bottomShadow.setBounds(0, getHeight() - getHeight() / visibleItems, getWidth(), getHeight());
        bottomShadow.draw(canvas);
    }

    /**
     * Draws value and label layout
     *
     * @param canvas the canvas for drawing
     */
    private void drawValue(Canvas canvas) {
        valuePaint.setColor(VALUE_TEXT_COLOR);
        valuePaint.drawableState = getDrawableState();

        Rect bounds = new Rect();
        itemsLayout.getLineBounds(visibleItems / 2, bounds);

        // draw label
        if (labelLayout != null) {
            canvas.save();
            canvas.translate(itemsLayout.getWidth() + LABEL_OFFSET, bounds.top);
            labelLayout.draw(canvas);
            canvas.restore();
        }

        // 繪製選中的item
        if (valueLayout != null) {
            canvas.save();
            canvas.translate(0, bounds.top + scrollingOffset);
            valueLayout.draw(canvas);
            canvas.restore();
        }
    }

    /**
     * 繪製普通Item
     *
     * @param canvas the canvas for drawing
     */
    private void drawItems(Canvas canvas) {
        canvas.save();

        int top = itemsLayout.getLineTop(1); //得到普通佈局中的第一行的高度
        canvas.translate(0, -top + scrollingOffset); //將畫布平移上去之後 再空出一個添加的位置

        itemsPaint.setColor(ITEMS_TEXT_COLOR);
        itemsPaint.drawableState = getDrawableState();
        itemsLayout.draw(canvas);

        canvas.restore(); //恢復畫布
    }

    /**
     * 繪製空間中心的矩形局域
     *
     * @param canvas the canvas for drawing
     */
    private void drawCenterRect(Canvas canvas) {
        int center = getHeight() / 2; //畫布的中點
        int offset = getItemHeight() / 2;  //列表項的寬度一半
        centerDrawable.setBounds(0, center - offset, getWidth(), center + offset);
        centerDrawable.draw(canvas);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        WheelAdapter adapter = getAdapter();
        if (adapter == null) {
            return true;
        }

        if (!gestureDetector.onTouchEvent(event) && event.getAction() == MotionEvent.ACTION_UP) {
            //手指鬆開時和手勢監聽器,沒有收到事件的時候
            justify();
        }
        return true;
    }

    /**
     * 滾動 這個 控件
     *
     * @param delta 需要滾動的距離
     */
    private void doScroll(int delta) {
        scrollingOffset += delta;  //0+199  ing 199

        int count = scrollingOffset / getItemHeight(); //需要滾動 的 item個數 因爲 要恰好停在 item上 不能偏嘛 如果取證
        int pos = currentItem - count;  //需要到達的位置


        if (isCyclic && adapter.getItemsCount() > 0) {
            // fix position by rotating 因爲存在一個 可以循環 滑動 所以處理一下這種情況 做一下 位置判斷
            while (pos < 0) {
                pos += adapter.getItemsCount();
            }
            pos %= adapter.getItemsCount();

        } else if (isScrollingPerformed) { //是否正在滾動呢
            //如果不是循環的
            if (pos < 0) {  //如果是負的  最多滾動 當前個數的距離 -> 滾動 到 0
                count = currentItem;
                pos = 0;
            } else if (pos >= adapter.getItemsCount()) { //如果要滾動的位置大於最多的位置
                count = currentItem - adapter.getItemsCount() + 1;
                pos = adapter.getItemsCount() - 1;
            }
        } else {
            // fix position  防止越界
            pos = Math.max(pos, 0); //保證 位置 大於0
            pos = Math.min(pos, adapter.getItemsCount() - 1); //保證 位置 小於 最大的
        }

        int offset = scrollingOffset;
        if (pos != currentItem) {
            setCurrentItem(pos, false);
        } else {
            invalidate();
        }


        // update offset
        scrollingOffset = offset - count * getItemHeight();  //恰好滾動的距離 可以使得 恰好在中間

        if (scrollingOffset > getHeight()) {  //去除了重複的次數
            scrollingOffset = scrollingOffset % getHeight() + getHeight();
        }
    }

    /**
     * 先清空消息隊列的所有信息 再將信息傳遞過去
     *
     * @param message the message to set
     */
    private void setNextMessage(int message) {
        clearMessages();
        animationHandler.sendEmptyMessage(message);
    }

    /**
     * 將消息隊列中過的信息進行清空處理
     */
    private void clearMessages() {
        animationHandler.removeMessages(MESSAGE_SCROLL);
        animationHandler.removeMessages(MESSAGE_JUSTIFY);
    }

    /**
     * Justifies wheel  整理版面 >>>>>>>>>>>>>
     */
    private void justify() {
        if (adapter == null) {
            return;
        }

        lastScrollY = 0;
        int offset = scrollingOffset;
        int itemHeight = getItemHeight();
        //判斷是否 需要去 微調 使得恰好 落在中間

        boolean needToIncrease = offset > 0 ? currentItem < adapter.getItemsCount() : currentItem > 0;
        //如果是循環的或者是不循環但是也是可以調整的狀態
        if ((isCyclic || needToIncrease) && Math.abs((float) offset) > (float) itemHeight / 2) {
            if (offset < 0) //
                offset += itemHeight + MIN_DELTA_FOR_SCROLLING;
            else
                offset -= itemHeight + MIN_DELTA_FOR_SCROLLING;
        }

        if (Math.abs(offset) > MIN_DELTA_FOR_SCROLLING) {
            //最後 滾動一丟丟
            scroller.startScroll(0, 0, 0, offset, SCROLLING_DURATION);
            //通知一下 動畫控制器
            setNextMessage(MESSAGE_JUSTIFY);
        } else {
            //不需要調整 可以有
            finishScrolling();
        }
    }

    /**
     * 開始滾動調用 開始滾動的方法
     */
    private void startScrolling() {
        if (!isScrollingPerformed) {
            isScrollingPerformed = true;
            notifyScrollingListenersAboutStart();
        }
    }

    /**
     * 停止 滾動 調用停止滾動的方法
     */
    void finishScrolling() {
        if (isScrollingPerformed) {
            notifyScrollingListenersAboutEnd();
            isScrollingPerformed = false;
        }
        invalidateLayouts();
        invalidate();
    }

    /**
     * Scroll the wheel
     *
     * @param
     * @param time scrolling duration
     */
    public void scroll(int itemsToScroll, int time) {
        scroller.forceFinished(true); //強制關了

        lastScrollY = scrollingOffset;
        int offset = itemsToScroll * getItemHeight();

        scroller.startScroll(0, lastScrollY, 0, offset - lastScrollY, time); //平滑滾動
        setNextMessage(MESSAGE_SCROLL); //通知去微調

        startScrolling(); //通知一下 開始 滾動 相關的監聽器得知道吧

    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章