Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android平台一款UI體驗好於NumberPicker的自定義控件NumberPickerView

Android平台一款UI體驗好於NumberPicker的自定義控件NumberPickerView

編輯:關於Android編程

NumberPickerView

another NumberPicker with more flexible attributes on Android platform

項目地址

https://github.com/Carbs0126/NumberPickerView

前言

在平時開發中會用到NumberPicker組件,但是默認風格的NumberPicker具有一些不靈活的屬性,且定制起來比較麻煩,且缺少一些過渡動效,因此在應用開發時,一般采用自定義的控件來完成選擇功能。

控件截圖

 


這裡寫圖片描述
靜態截圖以及漸變效果

 

 


這裡寫圖片描述
應用NumberPickerView的一個實例,一款可以選擇公歷/農歷日期的View,且公農歷自由切換,項目地址 GregorianLunarCalendar

 

 


這裡寫圖片描述
應用NumberPickerView的一個實例,一款可以選擇公歷/農歷日期的View,且公農歷自由切換

 <喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPmdpZrTz0KGzrLn9z97WxsHLo6y+38zl0Ke5+7/JvPujujxiciAvPg0KPGEgaHJlZj0="/uploadfile/2016/0630/20160630093223441.gif">/uploadfile/2016/0630/20160630093223441.gif

截屏有些問題,使得看上去有點卡頓且divider顏色不一致,實際效果很流暢。具體項目地址可見:
https://github.com/Carbs0126/GregorianLunarCalendar

說明

NumberPickerView是一款與android原生NumberPicker具有類似界面以及類似功能的View
主要功能同樣是從多個候選項中通過上下滾動的方式選擇需要的選項,但是與NumberPicker相比較,有幾個主要不同點,下面是兩者的不同之處。

原始控件特性-NumberPicker

顯示窗口只能顯示3個備選選項; 在fling時阻力較大,無法快速滑動; 在選中與非選中狀態切換比較生硬; 批量改變選項中的內容時,沒有動畫效果; 動態設置wrap模式時(setWrapSelectorWheel()方法),會有“暫時顯示不出部分選項”的問題; 選中位置沒有文字說明; 代碼中不能控制選項滑動滾動到某一item;

自定義控件特性-NumberPickerView

顯示窗口可以顯示多個備選選項; fling時滑動速度較快,且可以設置摩擦力; 在選中與非選中的狀態滑動時,具有漸變的動畫效果,包括文字放大縮小以及顏色的漸變; 在批量改變選項中的內容時,可以選擇是否采用友好的滑動效果; 可以動態的設置是否wrap,即,是否循環滾動; 選中位置可以添加文字說明,可控制文字字體大小顏色等; 具有在代碼中動態的滑動到某一位置的功能; 支持wrap_content,支持item的padding 提供多種屬性,優化UI效果 在滑動過程中不響應onValueChanged() 點擊上下單元格,可以自動滑動到對應的點擊對象。 兼容NumberPicker的重要方法和接口:
    兼容的方法有:
    setOnValueChangedListener()
    setOnScrollListener()
    setDisplayedValues()/getDisplayedValues()
    setWrapSelectorWheel()/getWrapSelectorWheel()
    setMinValue()/getMinValue()
    setMaxValue()/getMaxValue()
    setValue()/getValue()

    兼容的內部接口有:
    OnValueChangeListener
    OnScrollListener

使用方法

1.導入至工程

    compile 'cn.carbswang.android:NumberPickerView:1.0.2'

或者

    
      cn.carbswang.android
      NumberPickerView
      1.0.2
      pom
    

2.通過布局聲明NumberPickerView

    

3.Java代碼中使用:
1)若設置的數據(String[] mDisplayedValues)不會再次改變,可以使用如下方式進行設置:(與NumberPicker的設置方式一致)

        picker.setMinValue(minValue);
        picker.setMaxValue(maxValue);
        picker.setValue(value);

2)若設置的數據(String[] mDisplayedValues)會改變,可以使用如下組合方式進行設置:(與NumberPicker的更改數據方式一致)

        int minValue = getMinValue();
        int oldMaxValue = getMaxValue();
        int oldSpan = oldMaxValue - minValue + 1;
        int newMaxValue = display.length - 1;
        int newSpan = newMaxValue - minValue + 1;
        if (newSpan > oldSpan) {
            setDisplayedValues(display);
            setMaxValue(newMaxValue);
        } else {
            setMaxValue(newMaxValue);
            setDisplayedValues(display);
        }

或者直接使用NumberPickerView提供的方法:
refreshByNewDisplayedValues(String[] display)
使用此方法時需要注意保證數據改變前後的minValue值不變。

4.另外,NumberPickerView提供了平滑滾動的方法:
public void smoothScrollToValue(int fromValue, int toValue, boolean needRespond)

此方法與setValue(int)方法相同之處是可以動態設置當前顯示的item,不同之處在於此方法可以使NumberPickerView平滑的從滾動,即從fromValue值挑選最近路徑滾動到toValue,第三個參數needRespond用來標識在滑動過程中是否響應onValueChanged回調函數。因為多個NumberPickerView在聯動時,很可能不同的NumberPickerView的停止時間不同,如果在此時響應了onValueChanged回調,就可能再次聯動,造成數據不准確,將needRespond置為false,可避免在滑動中響應回調函數。

另外,在使用此方法或者間接調用此方法時,需要注意最好不要在onCreate(Bundle savedInstanceState)方法中調用,因為scroll動畫需要一定時間,如需確要在onCreate(Bundle savedInstanceState)中調用,請使用如下方式:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //代碼省略
        mNumberPickerView.post(new Runnable() {
            @Override
            public void run() {
                //調用smoothScrollToValue()等方法的代碼
            }
        });
    }

5.各項自定義屬性的說明

    
        //顯示的條目個數,默認3個
        //是否顯示兩條divider,默認顯示
        //兩條divider的顏色
        //divider距左側的距離
        //divider距右側的距離
        //divider的高度
        //未選中文字的顏色
        //選中文字的顏色
        //中間偏右側說明文字的顏色
        //未選中文字的大小
        //選中文字的大小
        //說明文字的大小
        //文字內容,stringarray類型
        //最小值,同setMinValue()
        //最大值,同setMaxValue()
        //設置是否wrap,同setWrapSelectorWheel
        //設置說明文字
        //空行的顯示文字,默認不顯示任何文字。只在WrapSelectorWheel==false是起作用
        //說明文字距離左側的距離,"左側"是指文字array最寬item的右側
        //說明文字距離右側的距離
        //item的水平padding,用於wrap_content模式
        //item的豎直padding,用於wrap_content模式

//以下屬性用於在wrap_content模式下,改變內容array並且又不想讓控件"跳動",那麼就可以設置所有改變的內容的最大寬度
        
        //可能達到的最大寬度,包括說明文字在內,最大寬度只可能比此String的寬度更大
        //可能達到的最大寬度,不包括說明文字在內,最大寬度只可能比此String的寬度+說明文字+說明文字marginstart +說明文字marginend 更大
        
        //說明文字的最大寬度
    

主要原理

一般情況下,我們只使用NumberPicker對文字進行選擇,很少涉及到添加不同的View甚至是圖片,因此,NumberPickerView只針對傳入的String[] 類型的內容通過onDraw(Canvas canvas)函數進行顯示。這裡主要涉及三個知識點:

1.滾動效果的產生:

Android的framework中帶有一個工具類Scroller,此類的功能是根據輸入值以及當前持續的時間,配合插值計算器,計算出當前的輸出值。輸入值一般是速度和坐標值,輸出值是坐標值,舉例來說,可以將其想像成高中物理根據初始速度、加速度和運行時間求取當前路程。Scroller的實現比較巧妙,它不會在startScroll()後去計算當前“路程”,而是只記錄“出發”時的各種狀態,只有當外部需要知道此時滑動到的位置時,才會去根據時間以及插值來進行計算,這種設計避免了另起線程實時計算等問題。
那麼問題來了,什麼時候去獲取scroller中當前的坐標值呢?閱讀View類的源碼,我們可以看到在View的boolean draw(Canvas canvas, ViewGroup parent, long drawingTime)函數中,有如下代碼:

if (!drawingWithRenderNode) {
    computeScroll();
    sx = mScrollX;
    sy = mScrollY;
}

即在繪制view時,先調用computeScroll(),因此我們可以在computeScroll()函數重新設置或刷新當前的坐標,並在onDraw(Canvas canvas)函數中根據當前的狀態重新繪制此view。

computeScroll()的使用方法通常為:

@Override
public void computeScroll() {
     //如果Scroller仍然在滾動
    if (mScroller.computeScrollOffset()) {
       //獲取mScroller中的值並計算和記錄,如 mGlobalY = mScroller.getCurrY();
       //刷新
        postInvalidate();
    }
}

在NumberPickerView中,我使用一個globalY值來記錄當前滾動到的坐標,並通過與Item的高度相除,計算當前應該顯示的item的偏移值,從而畫出當前只在顯示區域的item。

繪制的思路明確了,再來看滑動跟隨手指移動以及fling效果。這兩者均是通過override onTouchEvent(MotionEvent event)函數來實現的,不過實現的具體過程稍有不同:
(1). 跟隨手指滑動是復寫了MotionEvent.ACTION_MOVE的情況,根據當前手指移動的坐標值,計算globalY值,並刷新顯示。
(2). fling效果是通過VelocityTracker工具類實現的,此工具類可以根據接收的兩個連續發出的MotionEvent來獲取當前的滑動速度,若速度大於阈值,則將初始坐標值、VelocityTracker計算獲取的速度傳遞給Scroller.fling(...)函數,此時再次刷新,即可通過computeScroll()函數,獲取當前globalY值,進而更新繪圖。

2.自動校准位置。

為了實現自動校准,我在每次手指Up或者Scroller開始Fling時,均通過handler發送refresh消息,在接受到此消息時,計算當前位置是否處於被校准的位置,如果否,則計算需要滑動的位移,並將值傳遞給Scroller,進而進行校准滑動。當確保已經校准後,暫停發送refresh消息。

3.漸變的UI效果

漸變UI效果同樣是通過計算當前滑動的坐標以及某個item與中間顯示位置的差值比例,來確定此item中的字體大小以及顏色。

將NumberPicker改為NumberPickerView

要替代項目中使用的NumberPicker,只需要將涉及NumberPicker的代碼(如回調中傳入了NumberPicker、使用了NumberPicker的內部接口)改為NumberPickerView即可。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved