多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內(nèi)最全I(xiàn)T社區(qū)平臺 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁 > php開源 > 綜合技術(shù) > 【Android 應(yīng)用開發(fā)】 自定義組件 寬高適配方法, 手勢監(jiān)聽器操作組件, 回調(diào)接口維護(hù)策略, 繪制方法分析 -- 基于 WheelView 組件分析自定

【Android 應(yīng)用開發(fā)】 自定義組件 寬高適配方法, 手勢監(jiān)聽器操作組件, 回調(diào)接口維護(hù)策略, 繪制方法分析 -- 基于 WheelView 組件分析自定

來源:程序員人生   發(fā)布時間:2014-12-12 08:33:09 閱讀次數(shù):4652次


博客地址 http://blog.csdn.net/shulianghan/article/details/41520569


代碼下載

-- GitHub : https://github.com/han1202012/WheelViewDemo.git 

-- CSDN : http://download.csdn.net/detail/han1202012/8208997 ;



博客總結(jié) :

 

博文內(nèi)容 : 本文完全地分析了 WheelView 所有的源碼, 包括其適配器類型, 兩種回調(diào)接口 (選中條目改變回調(diào), 和開始結(jié)束轉(zhuǎn)動回調(diào)), 和詳細(xì)的分析了 WheelView 主題源碼, 其中 組件寬高丈量, 手勢監(jiān)聽器添加, 和精準(zhǔn)的繪圖方法是主要目的, 花了將近1周時間, 感覺很值, 在這里分享給大家;


WheelView 使用方法 : 創(chuàng)建 WheelView 組件 --> 設(shè)置顯示條目數(shù) --> 設(shè)置循環(huán) --> 設(shè)置適配器 --> 設(shè)置監(jiān)聽器 ;


自定義組件寬高獲得策略 : MeasureSpec 最大模式 取 默許值 和 給定值中較小的那個, 未定義模式取默許值, 精準(zhǔn)模式取 給定值;


自定義組件保護(hù)各種回調(diào)監(jiān)聽器策略 : 保護(hù)集合, 將監(jiān)聽器置于集合中, 回調(diào)接口時遍歷集合元素, 回調(diào)每一個元素的接口方法;


自定義組件手勢監(jiān)聽器添加方法 : 創(chuàng)建手勢監(jiān)聽器, 將手勢監(jiān)聽器傳入手勢探測器, 在 onTouchEvent() 方法中回調(diào)手勢監(jiān)聽器的 onTouchEvent()方法;



1. WheelView 簡介



1. WheelView 效果


在 Android 中實現(xiàn)類似與 IOS 的 WheelView 控件 : 如圖 




2. WheelView 使用流程



(1) 基本流程簡介

 

獲得組件 --> 設(shè)置顯示條目數(shù) --> 設(shè)置循環(huán) --> 設(shè)置適配器 --> 設(shè)置條目改變監(jiān)聽器 --> 設(shè)置轉(zhuǎn)動監(jiān)聽器


a. 創(chuàng)建 WheelView 組件 : 使用 構(gòu)造方法 或 從布局文件獲得 WheelView 組件;

b. 設(shè)置顯示條目數(shù) : 調(diào)用 WheelView 組件對象的 setVisibleItems 方法 設(shè)置;

c. 設(shè)置是不是循環(huán) : 設(shè)置 WheelView 是不是循環(huán), 調(diào)用 setCyclic() 方法設(shè)置;

d. 設(shè)置適配器 : 調(diào)用 WheelView 組件的 setAdapter() 方法設(shè)置;

e. 設(shè)置條目改變監(jiān)聽器 : 調(diào)用 WheelView 組件對象的 addChangingListener() 方法設(shè)置;

f. 設(shè)置轉(zhuǎn)動監(jiān)聽器 : 調(diào)用 WheelView 組件對象的 addScrollingListener() 方法設(shè)置;



(2) 代碼實例


a. 創(chuàng)建 WheelView 對象

//創(chuàng)建 WheelView 組件 final WheelView wheelLeft = new WheelView(context);

b. 設(shè)置 WheelView 顯示條目數(shù)

//設(shè)置 WheelView 組件最多顯示 5 個元素 wheelLeft.setVisibleItems(5);

c. 設(shè)置 WheelView 是不是轉(zhuǎn)動循環(huán)

//設(shè)置 WheelView 元素是不是循環(huán)轉(zhuǎn)動 wheelLeft.setCyclic(false);

d. 設(shè)置 WheelView 適配器

//設(shè)置 WheelView 適配器 wheelLeft.setAdapter(new ArrayWheelAdapter<String>(left));

e. 設(shè)置條目改變監(jiān)聽器

//為左邊的 WheelView 設(shè)置條目改變監(jiān)聽器 wheelLeft.addChangingListener(new OnWheelChangedListener() { @Override public void onChanged(WheelView wheel, int oldValue, int newValue) { //設(shè)置右邊的 WheelView 的適配器 wheelRight.setAdapter(new ArrayWheelAdapter<String>(right[newValue])); wheelRight.setCurrentItem(right[newValue].length / 2); } });

f. 設(shè)置轉(zhuǎn)動監(jiān)聽器

wheelLeft.addScrollingListener(new OnWheelScrollListener() { @Override public void onScrollingStarted(WheelView wheel) { // TODO Auto-generated method stub } @Override public void onScrollingFinished(WheelView wheel) { // TODO Auto-generated method stub } });




2. WheelView  適配器 監(jiān)聽器 相干接口分析



1. 適配器 分析



這里定義了1個適配器接口, 和兩個適配器類, 1個用于任意類型的數(shù)據(jù)集適配, 1個用于數(shù)字適配;


適配器操作 : 在 WheelView.java 中通過 setAdapter(WheelAdapter adapter) 和 getAdapter() 方法設(shè)置 獲得 適配器;

-- 適配器經(jīng)常使用操作 : 在 WheelView 中定義了 getItem(), getItemsCount(), getMaxmiumLength() 方法獲得 適配器的相干信息;

/** * 獲得該 WheelView 的適配器 * * @return * 返回適配器 */ public WheelAdapter getAdapter() { return adapter; } /** * 設(shè)置適配器 * * @param adapter * 要設(shè)置的適配器 */ public void setAdapter(WheelAdapter adapter) { this.adapter = adapter; invalidateLayouts(); invalidate(); }




(1) 適配器接口 ( interface WheelAdapter )


適配器接口WheelAdapter;

-- 接口作用 : 該接口是所有適配器的接口, 適配器類都需要實現(xiàn)該接口;


接口抽象方法介紹

-- getItemsCount() : 獲得適配器數(shù)據(jù)集合中元素個數(shù);

/** * 獲得條目的個數(shù) * * @return * WheelView 的條目個數(shù) */ public int getItemsCount();


-- getItem(int index) : 獲得適配器集合的中指定索引元素;

/** * 根據(jù)索引位置獲得 WheelView 的條目 * * @param index * 條目的索引 * @return * WheelView 上顯示的條目的值 */ public String getItem(int index);


-- getMaximumLength() : 獲得 WheelView 在界面上的顯示寬度;

/** * 獲得條目的最大長度. 用來定義 WheelView 的寬度. 如果返回 ⑴, 就會使用默許寬度 * * @return * 條目的最大寬度 或 ⑴ */ public int getMaximumLength();



(2) 數(shù)組適配器 ( class ArrayWheelAdapter<T> implements WheelAdapter )


適配器作用 : 該適配器可以傳入任何數(shù)據(jù)類型的數(shù)組, 可以是 字符串?dāng)?shù)組, 也能夠是任何對象的數(shù)組, 傳入的數(shù)組作為適配器的數(shù)據(jù)源;


成員變量分析

-- 數(shù)據(jù)源

/** 適配器的數(shù)據(jù)源 */ private T items[];


-- WheelView 最大寬度

/** WheelView 的寬度 */ private int length;



構(gòu)造方法分析

-- ArrayWheelAdapter(T items[], int length) : 傳入 T 類型 對象數(shù)組, 和 WheelView 的寬度;

/** * 構(gòu)造方法 * * @param items * 適配器數(shù)據(jù)源 集合 T 類型的數(shù)組 * @param length * 適配器數(shù)據(jù)源 集合 T 數(shù)組長度 */ public ArrayWheelAdapter(T items[], int length) { this.items = items; this.length = length; }


-- ArrayWheelAdapter(T items[]) : 傳入 T 類型對象數(shù)組, 寬度使用默許的寬度;

/** * 構(gòu)造方法 * * @param items * 適配器數(shù)據(jù)源集合 T 類型數(shù)組 */ public ArrayWheelAdapter(T items[]) { this(items, DEFAULT_LENGTH); }


實現(xiàn)的父類方法分析 :

--  getItem(int index) : 根據(jù)索引獲得數(shù)組中對應(yīng)位置的對象的字符串類型;

@Override public String getItem(int index) { //如果這個索引值合法, 就返回 item 數(shù)組對應(yīng)的元素的字符串情勢 if (index >= 0 && index < items.length) { return items[index].toString(); } return null; }


-- getItemsCount() : 獲得數(shù)據(jù)集廣大小, 直接返回數(shù)組大小;

@Override public int getItemsCount() { //返回 item 數(shù)組的長度 return items.length; }


-- getMaximumLength() : 獲得 WheelView 的最大寬度;

@Override public int getMaximumLength() { //返回 item 元素的寬度 return length; }



(3) 數(shù)字適配器 ( class NumericWheelAdapter implements WheelAdapter )


NumericWheelAdapter 適配器作用 : 數(shù)字作為 WheelView 適配器的顯示值;



成員變量分析

-- 最小值 : WheelView 數(shù)值顯示的最小值;

/** 設(shè)置的最小值 */ private int minValue;


-- 最大值 : WheelView 數(shù)值顯示的最大值;

/** 設(shè)置的最大值 */ private int maxValue;

-- 格式化字符串 : 用于字符串的格式化;

/** 格式化字符串, 用于格式化 貨幣, 科學(xué)計數(shù), 106進(jìn)制 等格式 */ private String format;


構(gòu)造方法分析

-- NumericWheelAdapter() : 默許的構(gòu)造方法, 使用默許的最大最小值;

/** * 默許的構(gòu)造方法, 使用默許的最大最小值 */ public NumericWheelAdapter() { this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE); }


-- NumericWheelAdapter(int minValue, int maxValue) : 傳入1個最大最小值;

/** * 構(gòu)造方法 * * @param minValue * 最小值 * @param maxValue * 最大值 */ public NumericWheelAdapter(int minValue, int maxValue) { this(minValue, maxValue, null); }


-- NumericWheelAdapter(int minValue, int maxValue, String format) : 傳入最大最小值, 和數(shù)字格式化方式;

/** * 構(gòu)造方法 * * @param minValue * 最小值 * @param maxValue * 最大值 * @param format * 格式化字符串 */ public NumericWheelAdapter(int minValue, int maxValue, String format) { this.minValue = minValue; this.maxValue = maxValue; this.format = format; }


實現(xiàn)的父類方法

-- 獲得條目 : 如果需要格式化, 先進(jìn)行格式化;

@Override public String getItem(int index) { String result = ""; if (index >= 0 && index < getItemsCount()) { int value = minValue + index; //如果 format 不為 null, 那末格式化字符串, 如果為 null, 直接返回數(shù)字 if(format != null){ result = String.format(format, value); }else{ result = Integer.toString(value); } return result; } return null; }

-- 獲得元素個數(shù)

@Override public int getItemsCount() { //返回數(shù)字總個數(shù) return maxValue - minValue + 1; }

-- 獲得 WheelView 最大寬度

@Override public int getMaximumLength() { //獲得 最大值 和 最小值 中的 較大的數(shù)字 int max = Math.max(Math.abs(maxValue), Math.abs(minValue)); //獲得這個數(shù)字 的 字符串情勢的 字符串長度 int maxLen = Integer.toString(max).length(); if (minValue < 0) { maxLen++; } return maxLen; }



2. 監(jiān)聽器相干接口



(1) 條目改變監(jiān)聽器 ( interface OnWheelChangedListener )


監(jiān)聽器作用 : 在 WheelView 條目改變的時候, 回調(diào)該監(jiān)聽器的接口方法, 履行條目改變對應(yīng)的操作;


接口方法介紹

-- onChanged(WheelView wheel, int oldValue, int newValue) : 傳入 WheelView 組件對象, 和 舊的 和 新的 條目值索引;

/** * 當(dāng)前條目改變時回調(diào)該方法 * * @param wheel * 條目改變的 WheelView 對象 * @param oldValue * WheelView 舊的條目值 * @param newValue * WheelView 新的條目值 */ void onChanged(WheelView wheel, int oldValue, int newValue);



(2) 轉(zhuǎn)動監(jiān)聽器 ( interface OnWheelScrollListener )


轉(zhuǎn)動監(jiān)聽器作用 : 在 WheelView 轉(zhuǎn)動動作 開始 和 結(jié)束的時候回調(diào)對應(yīng)的方法, 在對應(yīng)方法中進(jìn)行相應(yīng)的操作;



接口方法介紹

-- 開始轉(zhuǎn)動方法 : 在轉(zhuǎn)動開始的時候回調(diào)該方法;

/** * 在 WheelView 轉(zhuǎn)動開始的時候回調(diào)該接口 * * @param wheel * 開始轉(zhuǎn)動的 WheelView 對象 */ void onScrollingStarted(WheelView wheel);


-- 停止轉(zhuǎn)動方法 : 在轉(zhuǎn)動結(jié)束的時候回調(diào)該方法;

/** * 在 WheelView 轉(zhuǎn)動結(jié)束的時候回調(diào)該接口 * * @param wheel * 結(jié)束轉(zhuǎn)動的 WheelView 對象 */ void onScrollingFinished(WheelView wheel);



3. WheelView 解析



1. 觸摸 點擊 手勢 動作操作控制組件 模塊



(1) 創(chuàng)建手勢監(jiān)聽器


手勢監(jiān)聽器創(chuàng)建及對應(yīng)方法

-- onDown(MotionEvent e) : 在按下的時候回調(diào)該方法, e 參數(shù)是按下的事件;

-- onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) : 轉(zhuǎn)動的時候回調(diào)該方法, e1 轉(zhuǎn)動第1次按下事件, e2 當(dāng)前轉(zhuǎn)動的觸摸事件, X 上1次轉(zhuǎn)動到這1次轉(zhuǎn)動 x 軸距離, Y 上1次轉(zhuǎn)動到這1次轉(zhuǎn)動 y 軸距離;

-- onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) : 快速急沖轉(zhuǎn)動時回調(diào)的方法, e1 e2 與上面參數(shù)相同, velocityX 是手勢在 x 軸的速度, velocityY 是手勢在 y 軸的速度;

-- 代碼示例

/* * 手勢監(jiān)聽器監(jiān)聽到 轉(zhuǎn)動操作后回調(diào) * * 參數(shù)解析 : * MotionEvent e1 : 觸發(fā)轉(zhuǎn)動時第1次按下的事件 * MotionEvent e2 : 觸發(fā)當(dāng)前轉(zhuǎn)動的移動事件 * float distanceX : 自從上1次調(diào)用 該方法 到這1次 x 軸轉(zhuǎn)動的距離, * 注意不是 e1 到 e2 的距離, e1 到 e2 的距離是從開始轉(zhuǎn)動到現(xiàn)在的轉(zhuǎn)動距離 * float distanceY : 自從上1次回調(diào)該方法到這1次 y 軸轉(zhuǎn)動的距離 * * 返回值 : 如果事件成功觸發(fā), 履行完了方法中的操作, 返回true, 否則返回 false * (non-Javadoc) * @see android.view.GestureDetector.SimpleOnGestureListener#onScroll(android.view.MotionEvent, android.view.MotionEvent, float, float) */ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { //開始轉(zhuǎn)動, 并回調(diào)轉(zhuǎn)動監(jiān)聽器集合中監(jiān)聽器的 開始轉(zhuǎn)動方法 startScrolling(); doScroll((int) -distanceY); return true; } /* * 當(dāng)1個急沖手勢產(chǎn)生后 回調(diào)該方法, 會計算出該手勢在 x 軸 y 軸的速率 * * 參數(shù)解析 : * -- MotionEvent e1 : 急沖動作的第1次觸摸事件; * -- MotionEvent e2 : 急沖動作的移動產(chǎn)生的時候的觸摸事件; * -- float velocityX : x 軸的速率 * -- float velocityY : y 軸的速率 * * 返回值 : 如果履行終了返回 true, 否則返回false, 這個就是自己定義的 * * (non-Javadoc) * @see android.view.GestureDetector.SimpleOnGestureListener#onFling(android.view.MotionEvent, android.view.MotionEvent, float, float) */ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { //計算上1次的 y 軸位置, 當(dāng)前的條目高度 加上 剩余的 不夠1行高度的那部份 lastScrollY = currentItem * getItemHeight() + scrollingOffset; //如果可以循環(huán)最大值是無窮大, 不能循環(huán)就是條目數(shù)的高度值 int maxY = isCyclic ? 0x7FFFFFFF : adapter.getItemsCount() * getItemHeight(); int minY = isCyclic ? -maxY : 0; /* * Scroll 開始根據(jù)1個急沖手勢轉(zhuǎn)動, 轉(zhuǎn)動的距離與初速度有關(guān) * 參數(shù)介紹 : * -- int startX : 開始時的 X軸位置 * -- int startY : 開始時的 y軸位置 * -- int velocityX : 急沖手勢的 x 軸的初速度, 單位 px/s * -- int velocityY : 急沖手勢的 y 軸的初速度, 單位 px/s * -- int minX : x 軸轉(zhuǎn)動的最小值 * -- int maxX : x 軸轉(zhuǎn)動的最大值 * -- int minY : y 軸轉(zhuǎn)動的最小值 * -- int maxY : y 軸轉(zhuǎn)動的最大值 */ scroller.fling(0, lastScrollY, 0, (int) -velocityY / 2, 0, 0, minY, maxY); setNextMessage(MESSAGE_SCROLL); return true; } };



(2) 創(chuàng)建手勢探測器


手勢探測器創(chuàng)建 : 調(diào)用 其構(gòu)造函數(shù), 傳入 上下文對象 和 手勢監(jiān)聽器對象;

-- 制止長按操作 : 調(diào)用 setIsLongpressEnabled(false) 方法, 制止長按操作, 由于 長按操作會屏蔽轉(zhuǎn)動事件;

//創(chuàng)建1個手勢處理 gestureDetector = new GestureDetector(context, gestureListener); /* * 是不是允許長按操作, * 如果設(shè)置為 true 用戶按下不松開, 會返回1個長按事件, * 如果設(shè)置為 false, 按下不松開滑動的話 會收到轉(zhuǎn)動事件. */ gestureDetector.setIsLongpressEnabled(false);



(3) 將手勢探測器 與 組件結(jié)合


關(guān)聯(lián)手勢探測器 與 組件 : 在組件的 onTouchEvent(MotionEvent event) 方法中, 調(diào)用手勢探測器的 gestureDetector.onTouchEvent(event) 方法便可;

/* * 繼承自 View 的觸摸事件, 當(dāng)出現(xiàn)觸摸事件的時候, 就會回調(diào)該方法 * (non-Javadoc) * @see android.view.View#onTouchEvent(android.view.MotionEvent) */ @Override public boolean onTouchEvent(MotionEvent event) { //獲得適配器 WheelAdapter adapter = getAdapter(); if (adapter == null) { return true; } /* * gestureDetector.onTouchEvent(event) : 分析給定的動作, 如果可用, 調(diào)用 手勢檢測器的 onTouchEvent 方法 * -- 參數(shù)解析 : ev , 觸摸事件 * -- 返回值 : 如果手勢監(jiān)聽器成功履行了該方法, 返回true, 如果履行出現(xiàn)意外 返回 false; */ if (!gestureDetector.onTouchEvent(event) && event.getAction() == MotionEvent.ACTION_UP) { justify(); } return true; }




2. Scroller 簡介



(1) Scroller 簡介 


Scroller 通用作用 : Scroller 組件其實不是1個布局組件, 該組件是運行在后臺的, 通過1些方法設(shè)定 Scroller 對象 的操作 或 動畫, 然后讓 Scroller 運行在后臺中 用于摹擬轉(zhuǎn)動操作, 在適當(dāng)?shù)臅r機 獲得該對象的坐標(biāo)信息, 這些信息是在后臺運算出來的;


Scroller 在本 View 中作用 : Android 的這個自定義的 WheelView 組件, 可以平滑的轉(zhuǎn)動, 當(dāng)我們做1個加速滑動時, 會根據(jù)速度計算出滑動的距離, 這些數(shù)據(jù)都是在 Scroller 中計算出來的;



(2) 設(shè)定 Scroller 對象的動作參數(shù) 


終止轉(zhuǎn)動 : 

-- 終止轉(zhuǎn)動 跳轉(zhuǎn)到目標(biāo)位置 : 終止平緩的動畫, 直接跳轉(zhuǎn)到終究的 x y 軸的坐標(biāo)位置;

public void abortAnimation()

-- 終止轉(zhuǎn)動 停止在當(dāng)前位置 : 強行結(jié)束 Scroll 的轉(zhuǎn)動;

public final void forceFinished(boolean finished)


設(shè)置轉(zhuǎn)動參數(shù)

-- 設(shè)置終究 x 軸坐標(biāo)

public void setFinalX(int newX)

-- 設(shè)置終究 y 軸坐標(biāo)

public void setFinalY(int newY)

-- 設(shè)置轉(zhuǎn)動磨擦力

public final void setFriction(float friction)


設(shè)置動作

-- 開始轉(zhuǎn)動 : 傳入?yún)?shù) 開始 x 位置, 開始 y 位置, x 軸轉(zhuǎn)動距離, y 軸轉(zhuǎn)動距離;

public void startScroll(int startX, int startY, int dx, int dy)
-- 開始轉(zhuǎn)動 設(shè)定時間 : 最后1個參數(shù)是時間, 單位是 ms;

public void startScroll(int startX, int startY, int dx, int dy, int duration)
-- 急沖轉(zhuǎn)動 : 根據(jù)1個 急沖 手勢進(jìn)行轉(zhuǎn)動, 傳入?yún)?shù) : x軸開始位置, y軸開始位置, x 軸速度, y 軸速度, x 軸最小速度, x 軸最大速度, y 軸最小速度, y 軸最大速度;

public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY)


延長轉(zhuǎn)動時間 : 延長轉(zhuǎn)動的時間, 讓轉(zhuǎn)動滾的更遠(yuǎn)1些;

public void extendDuration(int extend)



(3) 獲得 Scroll 后臺運行參數(shù) 



獲得當(dāng)前數(shù)據(jù)

-- 獲得當(dāng)前 x 軸坐標(biāo)

public final int getCurrX()

-- 獲得當(dāng)前 y 軸坐標(biāo)

public final int getCurrY()

-- 獲得當(dāng)前速度

public float getCurrVelocity()


獲得開始結(jié)束時的數(shù)據(jù)  : 

-- 獲得開始 x 軸坐標(biāo)

public final int getStartX()

-- 獲得開始 y 軸坐標(biāo)

public final int getStartY()

-- 獲得終究 x 軸坐標(biāo) : 該參數(shù)只在急沖轉(zhuǎn)動時有效;

public final int getFinalX()

-- 獲得終究 y 軸坐標(biāo) : 該參數(shù)只在急沖轉(zhuǎn)動時有效;

public final int getFinalY()


查看是不是轉(zhuǎn)動終了

public final boolean isFinished()


獲得從開始轉(zhuǎn)動到現(xiàn)在的時間

public int timePassed()


獲得新位置 : 調(diào)用該方法可以獲得新位置, 如果返回 true 說明動畫還沒履行終了;

public boolean computeScrollOffset()



(4) Scroll 在 WheelView 中的應(yīng)用


Scroller 創(chuàng)建

//使用默許的 時間 和 插入器 創(chuàng)建1個轉(zhuǎn)動器 scroller = new Scroller(context);


手勢監(jiān)聽器 SimpleOnGestureListener 對象中的 onDown() 方法 : 如果轉(zhuǎn)動還在履行, 那末強行停止 Scroller 轉(zhuǎn)動;

//按下操作 public boolean onDown(MotionEvent e) { //如果轉(zhuǎn)動在履行 if (isScrollingPerformed) { //轉(zhuǎn)動強迫停止, 按下的時候不能繼續(xù)轉(zhuǎn)動 scroller.forceFinished(true); //清算信息 clearMessages(); return true; } return false; }


當(dāng)手勢監(jiān)聽器 SimpleOnGestureListener 對象中有急沖動作時 onFling() 方法中 : 手勢監(jiān)聽器監(jiān)聽到了 急沖動作, 那末 Scroller 也進(jìn)行對應(yīng)操作;

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { //計算上1次的 y 軸位置, 當(dāng)前的條目高度 加上 剩余的 不夠1行高度的那部份 lastScrollY = currentItem * getItemHeight() + scrollingOffset; //如果可以循環(huán)最大值是無窮大, 不能循環(huán)就是條目數(shù)的高度值 int maxY = isCyclic ? 0x7FFFFFFF : adapter.getItemsCount() * getItemHeight(); int minY = isCyclic ? -maxY : 0; /* * Scroll 開始根據(jù)1個急沖手勢轉(zhuǎn)動, 轉(zhuǎn)動的距離與初速度有關(guān) * 參數(shù)介紹 : * -- int startX : 開始時的 X軸位置 * -- int startY : 開始時的 y軸位置 * -- int velocityX : 急沖手勢的 x 軸的初速度, 單位 px/s * -- int velocityY : 急沖手勢的 y 軸的初速度, 單位 px/s * -- int minX : x 軸轉(zhuǎn)動的最小值 * -- int maxX : x 軸轉(zhuǎn)動的最大值 * -- int minY : y 軸轉(zhuǎn)動的最小值 * -- int maxY : y 軸轉(zhuǎn)動的最大值 */ scroller.fling(0, lastScrollY, 0, (int) -velocityY / 2, 0, 0, minY, maxY); setNextMessage(MESSAGE_SCROLL); return true; }


動畫控制 Handler 中

-- 轉(zhuǎn)動 : 獲得當(dāng)前 Scroller 的 y 軸位置, 與上1次的 y 軸位置對照, 如果 間距 delta 不為0, 就轉(zhuǎn)動;  

-- 查看是不是停止 : 如果現(xiàn)在距離 到 終究距離 小于最小轉(zhuǎn)動距離, 強迫停止;

-- 履行 msg.what 指令 : 如果需要停止, 強迫停止, 否則調(diào)劑坐標(biāo);

/** * 動畫控制器 * animation handler * * 可能會造成內(nèi)存泄漏 : 添加注解 HandlerLeak * Handler 類應(yīng)當(dāng)應(yīng)當(dāng)為static類型,否則有可能造成泄漏。 * 在程序消息隊列中排隊的消息保持了對目標(biāo)Handler類的利用。 * 如果Handler是個內(nèi)部類,那 么它也會保持它所在的外部類的援用。 * 為了不泄漏這個外部類,應(yīng)當(dāng)將Handler聲明為static嵌套類,并且使用對外部類的弱利用。 */ @SuppressLint("HandlerLeak") private Handler animationHandler = new Handler() { public void handleMessage(Message msg) { //回調(diào)該方法獲得當(dāng)前位置, 如果返回true, 說明動畫還沒有履行終了 scroller.computeScrollOffset(); //獲得當(dāng)前 y 位置 int currY = scroller.getCurrY(); //獲得已轉(zhuǎn)動了的位置, 使用上1次位置 減去 當(dāng)前位置 int delta = lastScrollY - currY; lastScrollY = currY; if (delta != 0) { //改變值不為 0 , 繼續(xù)轉(zhuǎn)動 doScroll(delta); } /* * 如果轉(zhuǎn)動到了指定的位置, 轉(zhuǎn)動還沒有停止 * 這時候需要強迫停止 */ if (Math.abs(currY - scroller.getFinalY()) < MIN_DELTA_FOR_SCROLLING) { currY = scroller.getFinalY(); scroller.forceFinished(true); } /* * 如果轉(zhuǎn)動沒有停止 * 再向 Handler 發(fā)送1個停止 */ if (!scroller.isFinished()) { animationHandler.sendEmptyMessage(msg.what); } else if (msg.what == MESSAGE_SCROLL) { justify(); } else { finishScrolling(); } } };




3. StaticLayout 布局容器



(1) StaticLayout 解析


StaticLayout 解析 : 該組件用于顯示文本, 1旦該文本被顯示后, 就不能再編輯, 如果想要修改文本, 使用 DynamicLayout 布局便可; 

-- 使用處景 : 1般情況下不會使用該組件, 當(dāng)想要自定義組件 或 想要使用 Canvas 繪制文本時 才使用該布局;



經(jīng)常使用方法解析

-- 獲得底部 Padding : 獲得底部 到最后1行文字的 間隔, 單位是 px;

public int getBottomPadding()

-- 獲得頂部 Padding

public int getTopPadding()

-- 獲得省略個數(shù) : 獲得某1行需要省略的字符個數(shù);

public int getEllipsisCount(int line)
-- 獲得省略開始位置 : 獲得某1行要省略的字符串的第1個位置索引;

public int getEllipsisStart(int line)
-- 獲得省略的寬度 : 獲得某1行省略字符串的寬度, 單位 px;

public int getEllipsisStart(int line)
-- 獲得是不是處理特殊符號

public boolean getLineContainsTab(int line)
-- 獲得文字的行數(shù)

public int getLineCount()
-- 獲得頂部位置 : 獲得某1行頂部的位置;

public int getLineTop(int line)
-- 獲得某1行底部位置

public int getLineDescent(int line)
-- 獲得行的方向 : 字符串從左至右 還是從右至左;

public final Directions getLineDirections(int line)
-- 獲得某行第1個字符索引 : 獲得的是 某1行 第1個字符 在全部字符串的索引;

public int getLineStart(int line)
-- 獲得該行段落方向 : 獲得該行文字方向, 左至右 或 右至左;

public int getParagraphDirection(int line)
-- 獲得某個垂直位置顯示的行數(shù)

public int getLineForVertical(int vertical)



(2) 布局顯示


布局創(chuàng)建

-- 3種布局 : WheelView 中觸及到了3種 StaticLayout 布局, 普通條目布局 itemLayout, 選中條目布局 valueLayout, 標(biāo)簽布局 labelLayout;

-- 創(chuàng)建時機 : 在 View 組件 每次 onMeasure() 和 onDraw() 方法中都要重新創(chuàng)建對應(yīng)布局;

-- 創(chuàng)建布局源碼

/** * 創(chuàng)建布局 * * @param widthItems * 布局條目寬度 * @param widthLabel * label 寬度 */ private void createLayouts(int widthItems, int widthLabel) { /* * 創(chuàng)建普通條目布局 * 如果 普通條目布局 為 null 或 普通條目布局的寬度 大于 傳入的寬度, 這時候需要重新創(chuàng)建布局 * 如果 普通條目布局存在, 并且其寬度小于傳入的寬度, 此時需要將 */ if (itemsLayout == null || itemsLayout.getWidth() > widthItems) { /* * android.text.StaticLayout.StaticLayout( * CharSequence source, TextPaint paint, * int width, Alignment align, * float spacingmult, float spacingadd, boolean includepad) * 傳入?yún)?shù)介紹 : * CharSequence source : 需要分行顯示的字符串 * TextPaint paint : 繪制字符串的畫筆 * int width : 條目的寬度 * Alignment align : Layout 的對齊方式, ALIGN_CENTER 居中對齊, ALIGN_NORMAL 左對齊, Alignment.ALIGN_OPPOSITE 右對齊 * float spacingmult : 行間距, 1.5f 代表 1.5 倍字體高度 * float spacingadd : 基礎(chǔ)行距上增加多少 , 真實行間距 等于 spacingmult 和 spacingadd 的和 * boolean includepad : */ itemsLayout = new StaticLayout(buildText(isScrollingPerformed), itemsPaint, widthItems, widthLabel > 0 ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_CENTER, 1, ADDITIONAL_ITEM_HEIGHT, false); } else { //調(diào)用 Layout 內(nèi)置的方法 increaseWidthTo 將寬度提升到指定的寬度 itemsLayout.increaseWidthTo(widthItems); } /* * 創(chuàng)建選中條目 */ 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); } /* * 創(chuàng)建標(biāo)簽條目 */ 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); } } }



4. 監(jiān)聽器管理



監(jiān)聽器集合保護(hù) 

-- 定義監(jiān)聽器集合 : 在 View 組件中 定義1個 List 集合, 集合中寄存 監(jiān)聽器元素;

/** 條目改變監(jiān)聽器集合 封裝了條目改變方法, 當(dāng)條目改變時回調(diào) */ private List<OnWheelChangedListener> changingListeners = new LinkedList<OnWheelChangedListener>(); /** 條目轉(zhuǎn)動監(jiān)聽器集合, 該監(jiān)聽器封裝了 開始轉(zhuǎn)動方法, 結(jié)束轉(zhuǎn)動方法 */ private List<OnWheelScrollListener> scrollingListeners = new LinkedList<OnWheelScrollListener>();


-- 提供對監(jiān)聽器集合的添加刪除接口 : 提供 對集合 進(jìn)行 添加 和 刪除的接口;

/** * 添加 WheelView 選擇的元素改變監(jiān)聽器 * * @param listener * the listener */ public void addChangingListener(OnWheelChangedListener listener) { changingListeners.add(listener); } /** * 移除 WheelView 元素改變監(jiān)聽器 * * @param listener * the listener */ public void removeChangingListener(OnWheelChangedListener listener) { changingListeners.remove(listener); }

-- 調(diào)用監(jiān)聽器接口

/** * 回調(diào)元素改變監(jiān)聽器集合的元素改變監(jiān)聽器元素的元素改變方法 * * @param oldValue * 舊的 WheelView選中的值 * @param newValue * 新的 WheelView選中的值 */ protected void notifyChangingListeners(int oldValue, int newValue) { for (OnWheelChangedListener listener : changingListeners) { listener.onChanged(this, oldValue, newValue); } }



5. 自定義 View 對象的寬高 



(1) onMeasure 方法 MeasureSpec 模式解析


常規(guī)處理方法 : 組件的寬高有3種情況, widthMeasureSpec 有3種模式 最大模式, 精準(zhǔn)模式, 未定義模式;

-- 最大模式 : 在 組件的寬或高 warp_content 屬性時, 會使用最大模式;

-- 精準(zhǔn)模式 : 當(dāng)給組件寬 或高 定義1個值 或 使用 match_parent 時, 會使用精準(zhǔn)模式;


處理寬高的常規(guī)代碼

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //獲得寬度 和 高度的模式 和 大小 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); Log.i(TAG, "寬度 : widthMode : " + getMode(widthMode) + " , widthSize : " + widthSize + " " + "高度 : heightMode : " + getMode(heightMode) + " , heightSize : " + heightSize); int width = 0; int height = 0; /* * 精準(zhǔn)模式 * 精準(zhǔn)模式下 高度就是精確的高度 */ if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; //未定義模式 和 最大模式 } else { //未定義模式下 獲得布局需要的高度 height = 100; //最大模式下 獲得 布局高度 和 布局所需高度的最小值 if (heightMode == MeasureSpec.AT_MOST) { height = Math.min(height, heightSize); } } if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else { width = 100; if (heightMode == MeasureSpec.AT_MOST) { width = Math.min(width, widthSize); } } Log.i(TAG, "終究結(jié)果 : 寬度 : " + width + " , 高度 : " + height); setMeasuredDimension(width, height); } public String getMode(int mode) { String modeName = ""; if(mode == MeasureSpec.EXACTLY){ modeName = "精準(zhǔn)模式"; }else if(mode == MeasureSpec.AT_MOST){ modeName = "最大模式"; }else if(mode == MeasureSpec.UNSPECIFIED){ modeName = "未定義模式"; } return modeName; }



(2) 測試上述代碼


使用下面的自定義組件測試

package cn.org.octopus.wheelview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.util.AttributeSet; import android.util.Log; import android.view.View; public class MyView extends View { public static final String TAG = "octopus.my.view"; public MyView(Context context, AttributeSet attrs) { super(context, attrs); } public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //獲得寬度 和 高度的模式 和 大小 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); Log.i(TAG, "寬度 : widthMode : " + getMode(widthMode) + " , widthSize : " + widthSize + " " + "高度 : heightMode : " + getMode(heightMode) + " , heightSize : " + heightSize); int width = 0; int height = 0; /* * 精準(zhǔn)模式 * 精準(zhǔn)模式下 高度就是精確的高度 */ if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; //未定義模式 和 最大模式 } else { //未定義模式下 獲得布局需要的高度 height = 100; //最大模式下 獲得 布局高度 和 布局所需高度的最小值 if (heightMode == MeasureSpec.AT_MOST) { height = Math.min(height, heightSize); } } if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else { width = 100; if (heightMode == MeasureSpec.AT_MOST) { width = Math.min(width, widthSize); } } Log.i(TAG, "終究結(jié)果 : 寬度 : " + width + " , 高度 : " + height); setMeasuredDimension(width, height); } public String getMode(int mode) { String modeName = ""; if(mode == MeasureSpec.EXACTLY){ modeName = "精準(zhǔn)模式"; }else if(mode
生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學(xué)習(xí)有所幫助,可以手機掃描二維碼進(jìn)行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 看一级毛片一区二区三区免费 | 国产精品日韩一区二区三区 | 在线观看国产福利 | a级做爰毛片视频免费看 | 性欧美videos护士 | 国产欧美亚洲三区久在线观看 | 国产精品国产三级国产 | 亚洲视频免费观看 | 2022精品福利在线小视频 | 欧美最猛黑人xxxx黑人 | 中文字幕精品视频在线观看 | 一区二区高清视频在线观看 | 欧美高清成人videosex | 中文字幕第15页 | 精品乱码一区二区三区在线 | 亚洲国产欧美日韩精品一区二区三区 | 国产精品欧美一区二区在线看 | 精品不卡一区中文字幕 | 亚洲a级在线观看 | 人喾交性专区免费 | 最近最新中文字幕免费大全 | 国产精品免费视频一区 | 手机在线中文字幕乱码免费 | a集毛片| 午夜精品福利影院 | 色精品一区二区三区 | 亚洲大片免费观看 | 亚洲精品不卡久久久久久 | 亚洲一区二区三 | 中文字幕欧美亚洲 | 尤物精品在线观看 | 日本一级不卡一二三区免费 | 日本不卡免费在线 | 国产毛片a精品毛 | 亚洲精品www久久久久久久软件 | 国产高清成人吃奶成免费视频 | 亚洲人成综合网站在线 | 久久精品国产欧美日韩亚洲 | 欧美日韩在线观看视频 | 欧美成人v视频免费看 | 福利视频美女国产精品 |