欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android UI設(shè)計(jì)系列之自定義ListView仿QQ空間阻尼下拉刷新和漸變菜單欄效果(8)

 更新時(shí)間:2021年10月03日 17:10:17   作者:llew2011  
這篇文章主要介紹了Android UI設(shè)計(jì)系列之自定義ListView仿QQ空間阻尼下拉刷新和漸變菜單欄效果,具有一定的實(shí)用性和參考價(jià)值,感興趣的小伙伴們可以參考一下

好久沒(méi)有寫(xiě)有關(guān)UI的博客了,剛剛翻了一下之前的博客,最近一篇有關(guān)UI的博客:Android UI設(shè)計(jì)系列之自定義Dialog實(shí)現(xiàn)各種風(fēng)格的對(duì)話框效果(7) ,實(shí)現(xiàn)各種風(fēng)格效果的對(duì)話框,在那篇博客寫(xiě)完后由于公司封閉開(kāi)發(fā)封網(wǎng)以及其它原因致使博客中斷至今,中斷這么久很是慚愧,后續(xù)我會(huì)盡量把該寫(xiě)的都補(bǔ)充出來(lái)。近來(lái)項(xiàng)目有個(gè)需求,要做個(gè)和QQ空間類似的菜單欄透明度漸變和下拉刷新帶有阻尼回彈的效果。于是花點(diǎn)時(shí)間動(dòng)手試了試,基本上達(dá)到了QQ空間的效果,截圖如下:

通過(guò)觀察QQ空間的運(yùn)行效果,發(fā)現(xiàn)當(dāng)往上滾動(dòng)時(shí)菜單欄會(huì)隨著滾動(dòng)距離的增大其透明度組件增大直到完全不透明,反之逐漸透明。當(dāng)滾動(dòng)到頂部后繼續(xù)下拉會(huì)出現(xiàn)拉升效果當(dāng)松手之后出現(xiàn)阻尼回彈效果。于是就通過(guò)重寫(xiě)ListView模仿了QQ空間的運(yùn)行效果。

實(shí)現(xiàn)QQ空間運(yùn)行效果前需要考慮兩個(gè)問(wèn)題:

1)、如何實(shí)現(xiàn)菜單欄透明度漸變

        通過(guò)觀察QQ空間的運(yùn)行效果可知其菜單欄的透明度是根據(jù)滾動(dòng)距離而動(dòng)態(tài)變化的,要想實(shí)現(xiàn)透明度的變化就需要知道的ListView的滾動(dòng)距離,所以有關(guān)透明度的問(wèn)題也就轉(zhuǎn)化成了滾動(dòng)距離的問(wèn)題。
2)、如何實(shí)現(xiàn)阻尼拉升和回彈效果

        要想利用ListView實(shí)現(xiàn)阻尼效果就要求ListView首先滾動(dòng)到了頂部,當(dāng)ListView滾動(dòng)到了頂部之后若繼續(xù)手動(dòng)下滑就要求其第一個(gè)Child變化來(lái)模擬下拉效果,當(dāng)手指松開(kāi)后該Child要回彈到初始狀態(tài)。
        我們先看第一個(gè)問(wèn)題:要想實(shí)現(xiàn)透明度漸變就要先獲取到ListView的滾動(dòng)距離,通過(guò)滾動(dòng)距離來(lái)計(jì)算相應(yīng)的透明度。由于ListView的復(fù)用機(jī)制就決定了不能通過(guò)第一個(gè)可見(jiàn)Item的getTop()方法來(lái)得到滾動(dòng)值,所以我們可以通過(guò)HeaderView來(lái)獲取滾動(dòng)距離,因?yàn)镠eader在ListView中是不參與復(fù)用的。

下面先了解一下ListView添加HeaderView后的滾動(dòng)流程:

上圖大致畫(huà)了ListView含有HeaderView時(shí)的三個(gè)滾動(dòng)狀態(tài),狀態(tài)一可稱為初始狀態(tài)或者是恰好滾動(dòng)到最頂部狀態(tài),此時(shí)HeaderView的getTop()值為0;狀態(tài)二為L(zhǎng)istView的滾動(dòng)中狀態(tài),此時(shí)HeaderView沒(méi)有完全滾動(dòng)出ListView邊界,getTop()的返回值為負(fù)數(shù)且其絕對(duì)值范圍在0和HeaderView的高度之間;狀態(tài)三表示的是HeaderView完全滾動(dòng)出了ListView邊界,若調(diào)用getTop()得到的返回值為負(fù)數(shù)且絕對(duì)值等于HeaderView的高度(此后可理解成HeaderView一直固定在ListView的頂部)。

明白了ListView的滾動(dòng)原理,我們先嘗試實(shí)現(xiàn)漸變菜單欄的功能。首先定義自己的ListView,取名為FlexibleListView,單詞flexible是靈活的、多樣的的意思,因?yàn)槲覀兊腖istView不僅要實(shí)現(xiàn)菜單欄的透明度漸變還要實(shí)現(xiàn)阻尼效果,所以取名為FlexibleListView比較恰當(dāng)。FlexibleListView繼承ListView后需要實(shí)現(xiàn)其構(gòu)造方法,代碼如下:

public class FlexibleListView extends ListView { 
 
 public FlexibleListView(Context context) { 
 super(context); 
 } 
 
 public FlexibleListView(Context context, AttributeSet attrs) { 
 super(context, attrs); 
 } 
 
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr) { 
 super(context, attrs, defStyleAttr); 
 } 
 
 @TargetApi(21) 
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 
 super(context, attrs, defStyleAttr, defStyleRes); 
 } 
} 

FlexibleListView僅僅是繼承了ListView,這本質(zhì)上和ListView沒(méi)有區(qū)別。既然我們是通過(guò)給ListView添加HeaderView的方式來(lái)判斷滾動(dòng)距離,那就要獲取到HeaderView對(duì)象。怎么獲取到HeaderView對(duì)象呢?這里有個(gè)技巧,由于給ListView添加HeaderView最終是調(diào)用ListView的addHeaderView(View v, Object data, boolean isSelected)方法,所以我們可以重寫(xiě)該方法,取到添加進(jìn)來(lái)的第一個(gè)HeaderView,那怎么判斷是第一個(gè)添加進(jìn)來(lái)的HeaderView呢?因?yàn)镠eaderView的添加是有序的即先添加的先繪制。所以可以定義一個(gè)代表第一個(gè)HeaderView的屬性mHeaderView,當(dāng)調(diào)用到addHeaderView()方法時(shí)通過(guò)判斷mHeaderView的值是否為空,如果為空就賦值否則不賦值,代碼如下:

public class FlexibleListView extends ListView { 
 
 private View mHeaderView; 
 private int mMaxScrollHeight; 
 
 public FlexibleListView(Context context) { 
 super(context); 
 } 
 
 public FlexibleListView(Context context, AttributeSet attrs) { 
 super(context, attrs); 
 } 
 
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr) { 
 super(context, attrs, defStyleAttr); 
 } 
 
 @TargetApi(21) 
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 
 super(context, attrs, defStyleAttr, defStyleRes); 
 } 
 
 @Override 
 public void addHeaderView(View v, Object data, boolean isSelectable) { 
 super.addHeaderView(v, data, isSelectable); 
 if(null == mHeaderView) { 
 mHeaderView = v; 
 mMaxScrollHeight = mHeaderView.getLayoutParams().height; 
 } 
 } 
} 

FlexibleListView中定義了mHeaderView和mMaxScrollHeight屬性,在addHeaderView()方法中對(duì)mHeaderView做非空判斷來(lái)獲取到第一個(gè)HeaderView并賦值給mHeadereView,mMaxScrollHeight表示HeaderView的最大滾動(dòng)距離,當(dāng)HeaderView的滾動(dòng)距離超過(guò)此值我們就要設(shè)置菜單欄不透明否則就更改透明度。在這里我直接使用了HeaderView的高度來(lái)表示其允許滾動(dòng)的最大距離。

現(xiàn)在可以獲取到ListView的第一個(gè)HeaderView,接下來(lái)就是判斷ListView的滾動(dòng)了,這時(shí)候有的童靴可能會(huì)想到采用給ListView添加ScrollListener的方式,這種方式是可行的,但我們這次不采用添加Listener的方式,如果你對(duì)ListView的源碼比較熟悉的話就清楚觸發(fā)OnItemScrollListener的回調(diào)時(shí)機(jī)是在AbsListView的invokeOnItemScrollListener()方法中,該方法源碼如下:

/** 
 * Notify our scroll listener (if there is one) of a change in scroll state 
 */ 
void invokeOnItemScrollListener() { 
 if (mFastScroll != null) { 
 mFastScroll.onScroll(mFirstPosition, getChildCount(), mItemCount); 
 } 
 if (mOnScrollListener != null) { 
 mOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), mItemCount); 
 } 
 onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these. 
} 

invokeOnItemScrollListener()方法就是觸發(fā)滾動(dòng)回調(diào)的,無(wú)論我們給不給ListView設(shè)置OnItemScrollListener那該方法都會(huì)調(diào)用,細(xì)心的同學(xué)可能發(fā)現(xiàn)在該方法最后調(diào)用了View的onScrollChanged()方法,這時(shí)候你恍然大悟,我們可以重寫(xiě)該方法呀,當(dāng)ListView發(fā)生滾動(dòng)了也就調(diào)用了onScrollChange()方法,多省事呀。呵呵,恭喜你,答對(duì)了,我們今天就是采用重寫(xiě)onScrollChanged()方法并在該方法中通過(guò)判斷ListView的HeaderView的滾動(dòng)距離來(lái)設(shè)置菜單欄的透明度的。

現(xiàn)在我們清楚了ListView的滾動(dòng)時(shí)機(jī),也有了HeaderView和最大滾動(dòng)距離,接下來(lái)就是分析實(shí)現(xiàn)漸變的條件了:要實(shí)現(xiàn)漸變我們就要清楚是誰(shuí)要漸變,在我們的APP中可能是ActionBar,也可能是ToolBar,還有可能是我們自定義的一個(gè)ViewGroup來(lái)模擬的ActionBar,所以FlexibleListView得有個(gè)代表ActionBar的mActionBar屬性并對(duì)外提供一個(gè)方法bindActionBar(),該方法就表示把需要實(shí)現(xiàn)漸變的ActionBar傳遞進(jìn)來(lái),代碼如下:

public class FlexibleListView extends ListView { 
 
 private View mActionBar; 
 private View mHeaderView; 
 private int mMaxScrollHeight; 
 private Drawable mActionBarBackground; 
 
 public FlexibleListView(Context context) { 
 super(context); 
 } 
 
 public FlexibleListView(Context context, AttributeSet attrs) { 
 super(context, attrs); 
 } 
 
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr) { 
 super(context, attrs, defStyleAttr); 
 } 
 
 @TargetApi(21) 
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 
 super(context, attrs, defStyleAttr, defStyleRes); 
 } 
 
 @Override 
 protected void onScrollChanged(int l, int t, int oldl, int oldt) { 
 super.onScrollChanged(l, t, oldl, oldt); 
 if(null != mActionBarBackground) { 
 mActionBarBackground.setAlpha(evaluateAlpha(Math.abs(mHeaderView.getTop()))); 
 } 
 } 
 
 @Override 
 public void addHeaderView(View v, Object data, boolean isSelectable) { 
 super.addHeaderView(v, data, isSelectable); 
 if(null == mHeaderView) { 
 mHeaderView = v; 
 mMaxScrollHeight = mHeaderView.getLayoutParams().height; 
 } 
 } 
 
 private int evaluateAlpha(int t) { 
 if (t >= mMaxScrollHeight) { 
 return 255; 
 } 
 return (int) (255 * t /(float) mMaxScrollHeight); 
 } 
 
 public void bindActionBar(View actionBar) { 
 if(null != actionBar) { 
 mActionBar = actionBar; 
 mActionBarBackground = actionBar.getBackground(); 
 if(null == mActionBarBackground) { 
 mActionBarBackground = new ColorDrawable(Color.TRANSPARENT); 
 } 
 mActionBarBackground.setAlpha(0); 
 if(Build.VERSION.SDK_INT >= 16) { 
 mActionBar.setBackground(mActionBarBackground); 
 } else { 
 mActionBar.setBackgroundDrawable(mActionBarBackground); 
 } 
 } 
 } 
 
 public void bindActionBar(ActionBar actionBar) { 
 if(null != actionBar) { 
 // TODO impl with ActionBar 
 // actionBar.setBackgroundDrawable(); 
 } 
 } 
} 

FlexibleListView新增了mActionBar和mActionBarBackground屬性,mActionBar代表需要漸變的菜單欄,mActionBarBackground為菜單欄的背景。其次對(duì)外提供了重載方法bindActionBar(),參數(shù)為ActionBar的方法是空實(shí)現(xiàn),里邊添加了TODO提示符并給了setBackgroundDrawable()提示(注意ActionBar實(shí)現(xiàn)漸變需要設(shè)置WindowFeature),希望童靴們自己可以實(shí)現(xiàn)出來(lái)。

FlexibleListView中重寫(xiě)了onScrollChanged()方法,在該方法中通過(guò)獲取mHeaderView的getTop()值然后調(diào)用evaluateAlpha()方法計(jì)算出alpha值,evaluateAlpha()的計(jì)算很簡(jiǎn)單,當(dāng)滾動(dòng)值超過(guò)了最大滾動(dòng)距離mMaxScrollHeight就返回255(255表示不透明,0表示透明),否則計(jì)算出當(dāng)前滾動(dòng)值所對(duì)應(yīng)的alpha值,最后通過(guò)調(diào)用mActionBarBackground的setAlpha()來(lái)達(dá)到mActionBar的透明度變化。

現(xiàn)在實(shí)現(xiàn)菜單欄的透明度的邏輯準(zhǔn)備就緒了,我們先測(cè)試一下看看,定義菜單欄布局,代碼如下:

<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="match_parent" 
 android:layout_height="@dimen/action_bar_height" 
 android:background="#aabbcc" 
 android:clickable="true" 
 android:orientation="vertical" 
 android:paddingLeft="10dp"> 
 
 <TextView 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_gravity="center_vertical" 
 android:drawableLeft="@mipmap/back" 
 android:text="動(dòng)態(tài)" 
 android:textColor="#b8e7fe" 
 android:textSize="17sp" /> 
 
 <TextView 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:layout_gravity="center" 
 android:text="好友動(dòng)態(tài)" 
 android:textColor="#b8e7fe" 
 android:textSize="17sp" /> 
 
</FrameLayout>

菜單欄包含一個(gè)返回按鈕和一個(gè)標(biāo)題,并且給菜單欄設(shè)置了固定高度和背景色,然后布局我們的activity_main.xml文件,代碼如下:

<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 xmlns:tools="http://schemas.android.com/tools" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent"> 
 
 <com.llew.wb.git.qqzone.FlexibleListView 
 android:id="@+id/flexible_list_view" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:scrollbars="none"></com.llew.wb.git.qqzone.FlexibleListView> 
 
 <include 
 android:id="@+id/custom_action_bar" 
 layout="@layout/action_bar_layout"/> 
</FrameLayout> 

activity_main.xml的布局文件很簡(jiǎn)單,采用FrameLayout根布局讓菜單欄懸浮在FlexibleListView上邊,然后編寫(xiě)我們的MainActivity代碼,如下所示:

public class MainActivity extends AppCompatActivity { 
 
 private FlexibleListView mListView; 
 
 @Override 
 protected void onCreate(Bundle savedInstanceState) { 
 super.onCreate(savedInstanceState); 
 setContentView(R.layout.activity_main); 
 
 initGlobalParams(); 
 } 
 
 private void initGlobalParams() { 
 mListView = (FlexibleListView) findViewById(R.id.flexible_list_view); 
 
 View mFlexibleHeaderView = new View(getApplicationContext()); 
 mFlexibleHeaderView.setBackgroundColor(Color.parseColor("#bbaacc")); 
 int height = getResources().getDimensionPixelSize(R.dimen.header_height); 
 LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, height); 
 mFlexibleHeaderView.setLayoutParams(params); 
 
 final View actionBar = findViewById(R.id.custom_action_bar); 
 
 mListView.bindActionBar(actionBar); 
 mListView.addHeaderView(mFlexibleHeaderView); 
 
 mListView.setAdapter(new Adapter()); 
 } 
 
 static class Adapter extends BaseAdapter { 
 @Override 
 public int getCount() { 
 return 80; 
 } 
 
 @Override 
 public Object getItem(int position) { 
 return null; 
 } 
 
 @Override 
 public long getItemId(int position) { 
 return 0; 
 } 
 
 @Override 
 public View getView(int position, View convertView, ViewGroup parent) { 
 TextView textView = new TextView(parent.getContext()); 
 textView.setPadding(50, 50, 50, 50); 
 textView.setText(position + 10 + ""); 
 return textView; 
 } 
 } 
} 

在MainActivity中給FlexibleListView添加了一個(gè)固定高度背景色為"#bbaacc"的Header,并把懸浮菜單欄actionBar賦值給了FlexibleListView的mActionBar,最后設(shè)置Adapter,為了測(cè)試代碼寫(xiě)的很簡(jiǎn)單,我們運(yùn)行一下程序,看看效果:

看到運(yùn)行效果好開(kāi)心呀,(*^__^*) ……透明度漸變功能達(dá)到了我們的預(yù)期,接下來(lái)開(kāi)始實(shí)現(xiàn)阻尼效果,阻尼效果就是當(dāng)ListView滾動(dòng)到了頂部此時(shí)若繼續(xù)下滑,ListView能夠繼續(xù)往下滾動(dòng)一段距離當(dāng)手指離開(kāi)屏幕后ListView要恢復(fù)原位置。為了實(shí)現(xiàn)這個(gè)功能有的童靴可能會(huì)想到重寫(xiě)有關(guān)事件傳遞的onXXXEvent()等方法,之后在MotionEvent為DOWN,MOVE,UP或者CANCEL條件下分別做邏輯判斷來(lái)實(shí)現(xiàn)阻尼效果,此方式可行,但是和今天我們的實(shí)現(xiàn)相比起來(lái)復(fù)雜了許多....

這里所實(shí)現(xiàn)阻尼效果所采用的方法是利用View的overScrollBy()方法,有的童靴可能會(huì)問(wèn)overScrollBy()方法是2.3版本之后才增加的,2.3版本之前的兼容性怎么辦?我實(shí)現(xiàn)這個(gè)功能之前也考慮過(guò)這個(gè)問(wèn)題,一方面我們公司的APP只支持3.0以上版本,另一方面2.3及以前的版本市場(chǎng)占有率幾乎微乎其微了,所以可以考慮不再兼容2.3以前的老版本。

有的同學(xué)或許對(duì)overScrollBy()方法比較陌生,先大致說(shuō)一下該方法,其源碼如下:

/** 
 * Scroll the view with standard behavior for scrolling beyond the normal 
 * content boundaries. Views that call this method should override 
 * {@link #onOverScrolled(int, int, boolean, boolean)} to respond to the 
 * results of an over-scroll operation. 
 * 
 * Views can use this method to handle any touch or fling-based scrolling. 
 * 
 * @param deltaX Change in X in pixels 
 * @param deltaY Change in Y in pixels 
 * @param scrollX Current X scroll value in pixels before applying deltaX 
 * @param scrollY Current Y scroll value in pixels before applying deltaY 
 * @param scrollRangeX Maximum content scroll range along the X axis 
 * @param scrollRangeY Maximum content scroll range along the Y axis 
 * @param maxOverScrollX Number of pixels to overscroll by in either direction 
 * along the X axis. 
 * @param maxOverScrollY Number of pixels to overscroll by in either direction 
 * along the Y axis. 
 * @param isTouchEvent true if this scroll operation is the result of a touch event. 
 * @return true if scrolling was clamped to an over-scroll boundary along either 
 * axis, false otherwise. 
 */ 
@SuppressWarnings({"UnusedParameters"}) 
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, 
 int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 
 // ...... 
} 

閱讀源碼看注釋很重要,我們先看一下注釋,大致意思如下:

        當(dāng)View組件滾動(dòng)到邊界時(shí)還會(huì)繼續(xù)進(jìn)行之前的滾動(dòng)操作(注意:沒(méi)有滾動(dòng)到邊界時(shí)是不會(huì)觸發(fā)該方法的),如果View組件調(diào)用了該方法那么View組件就應(yīng)該重寫(xiě)onOverScrolled()方法來(lái)響應(yīng)over-scroll操作。View控件可以調(diào)用該方法處理任何的觸摸滾動(dòng)或者是快速滑動(dòng)等。感覺(jué)翻譯的好別扭,說(shuō)的直白點(diǎn)就是當(dāng)ListView,ScrollView等滾動(dòng)到頭了若繼續(xù)下滑就會(huì)調(diào)用該方法。
        overScrollBy()方法有9個(gè)參數(shù),每個(gè)參數(shù)注釋都說(shuō)的很詳細(xì),我們只看需要用到的倆參數(shù)deltaY和isTouchEvent;deltaY表示的是在Y軸上滾動(dòng)的相對(duì)值,比如ListView滾動(dòng)到了頂部此時(shí)如果繼續(xù)下拉,deltaY值為負(fù)數(shù),當(dāng)其滾動(dòng)到了最底部當(dāng)我們繼續(xù)上拉,deltaY值為正數(shù),所以我們可以根據(jù)deltaY判斷ListView是上拉操作還是下拉操作,isTouchEvent為true表示手指在觸摸屏幕否則離開(kāi)屏幕。
        了解overScrollBy()方法后開(kāi)始實(shí)現(xiàn)阻尼效果,核心就是重寫(xiě)overScrollBy()方法,在該方法中動(dòng)態(tài)改變HeaderView的高度,若手指松開(kāi)我們就復(fù)原HeaderView。我們知道QQ空間頂部是一張圖片,當(dāng)下拉的時(shí)候該圖片有彈性拉升效果,當(dāng)手指松開(kāi)后圖片又伸縮回去了,所以我們就直接用ImageView模擬此效果。模擬圖片阻尼可以讓ImageView的寬高為MATCH_PARENT(HeaderView的高度改變之后ImageView的高度也可以隨之更改),這個(gè)時(shí)候還要設(shè)置ImageView的scaleType為CENTER_CROP(不清楚ImageView的scaleType屬性可參照我之前寫(xiě)的一篇博文:Android 源碼系列之<一>從源碼的角度深入理解ImageView的ScaleType屬性)。

現(xiàn)在開(kāi)始在FlexibleListView中重寫(xiě)overScrollBy()方法,代碼如下:

@Override 
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 
 if(null != mHeaderView) { 
 if(isTouchEvent && deltaY < 0) { 
 mHeaderView.getLayoutParams().height += Math.abs(deltaY / 3.0); 
 mHeaderView.requestLayout(); 
 } 
 } 
 return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 
} 

overScrollBy()方法中我們根據(jù)deltaY值動(dòng)態(tài)的更改了mHeaderView的高度并重新布局達(dá)到更改ImageView高度的目的,注意:計(jì)算高度的時(shí)候用了deltaY除以3,此時(shí)的3表示增長(zhǎng)因子,目的是讓HeaderView緩慢的增長(zhǎng),這里可以對(duì)外提供一個(gè)方法來(lái)設(shè)置此值。

現(xiàn)在僅實(shí)現(xiàn)了HeaderView的拉升功能,但是還沒(méi)有實(shí)現(xiàn)縮放功能,因?yàn)閛verScrollBay()中實(shí)現(xiàn)的是手指觸摸的下拉,當(dāng)手指離開(kāi)屏幕后要進(jìn)行HeaderView的復(fù)原操作,所以我們可以在考慮在onTouchEvent()方法中判斷MotionEvent的類型,當(dāng)為UP或者CANCEL時(shí)就復(fù)原HeaderView,復(fù)原HeaderView不能一下子復(fù)原而是要用動(dòng)畫(huà)的方式,這樣看上去才比較自然,所以onTouchEvent()代碼如下:

@Override 
public boolean onTouchEvent(MotionEvent ev) { 
 if(null != mHeaderView) { 
 int action = ev.getAction(); 
 if(MotionEvent.ACTION_UP == action || MotionEvent.ACTION_CANCEL == action) { 
 resetHeaderViewHeight(); 
 } 
 } 
 return super.onTouchEvent(ev); 
} 
 
private void resetHeaderViewHeight() { 
 ValueAnimator valueAnimator = ValueAnimator.ofInt(1); 
 valueAnimator.setDuration(700); 
 valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
 @Override 
 public void onAnimationUpdate(ValueAnimator animation) { 
 final float f = animation.getAnimatedFraction(); 
 mHeaderView.getLayoutParams().height -= f * (mHeaderView.getLayoutParams().height - mMaxScrollHeight); 
 mHeaderView.requestLayout(); 
 
 } 
 }); 
 valueAnimator.setInterpolator(new OvershootInterpolator()); 
 valueAnimator.start(); 
} 

HeaderView的復(fù)原動(dòng)畫(huà)我們采用了ValueAnimator,當(dāng)動(dòng)畫(huà)執(zhí)行過(guò)程中我們動(dòng)態(tài)的更改HeaderView的值來(lái)達(dá)到漸變效果。接下來(lái)布局HeaderView來(lái)模擬QQ空間,代碼如下:

<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="match_parent" 
 android:layout_height="@dimen/header_height"> 
 
 <ImageView 
 android:id="@+id/iv" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:scaleType="centerCrop" 
 android:src="@mipmap/ttt" /> 
 
 <LinearLayout 
 android:layout_width="match_parent" 
 android:layout_height="30dp" 
 android:layout_gravity="bottom" 
 android:background="#33333333" 
 android:gravity="center_vertical" 
 android:orientation="horizontal"> 
 
 <TextView 
 android:layout_width="0dp" 
 android:layout_height="match_parent" 
 android:layout_weight="1" 
 android:text="相冊(cè)" 
 android:gravity="center" 
 android:textColor="@android:color/white" /> 
 
 <View 
 android:layout_width="1dp" 
 android:layout_height="20dp" 
 android:background="#ffffff" /> 
 
 <TextView 
 android:layout_width="0dp" 
 android:layout_height="match_parent" 
 android:layout_weight="1" 
 android:text="說(shuō)說(shuō)" 
 android:gravity="center" 
 android:textColor="@android:color/white" /> 
 
 <View 
 android:layout_width="1dp" 
 android:layout_height="20dp" 
 android:background="#ffffff" /> 
 
 <TextView 
 android:layout_width="0dp" 
 android:layout_height="match_parent" 
 android:layout_weight="1" 
 android:text="個(gè)性化" 
 android:gravity="center" 
 android:textColor="@android:color/white" /> 
 
 <View 
 android:layout_width="1dp" 
 android:layout_height="20dp" 
 android:background="#ffffff" /> 
 
 <TextView 
 android:layout_width="0dp" 
 android:layout_height="match_parent" 
 android:layout_weight="1" 
 android:text="\@ 與我相關(guān)" 
 android:gravity="center" 
 android:textColor="@android:color/white" /> 
 
 </LinearLayout> 
</FrameLayout> 

HeaderView的布局中讓ImageView的寬高都設(shè)置成了match_parent并且把scaleType設(shè)置為centerCrop。修改MainActivity的initGlobalParams()方法,代碼如下:

void initGlobalParams() { 
 mListView = (FlexibleListView) findViewById(R.id.flexible_list_view); 
 View mFlexibleHeaderView = LayoutInflater.from(this).inflate(R.layout.flexible_header_layout, mListView, false); 
 AbsListView.LayoutParams params = (AbsListView.LayoutParams)mFlexibleHeaderView.getLayoutParams(); 
 if(null == params) { 
 params = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.WRAP_CONTENT); 
 } 
 params.height = getResources().getDimensionPixelSize(R.dimen.header_height); 
 mFlexibleHeaderView.setLayoutParams(params); 
 
 final View actionBar = findViewById(R.id.custom_action_bar); 
 
 mListView.bindActionBar(actionBar); 
 mListView.addHeaderView(mFlexibleHeaderView); 
 
 mListView.setAdapter(new Adapter()); 
} 

OK,一切都準(zhǔn)備就緒,趕緊運(yùn)行一下程序,看看效果吧(*^__^*) ……

恩,看上去效果還不錯(cuò)......

好了,有關(guān)實(shí)現(xiàn)QQ空間的阻尼下拉刷新和漸變菜單欄就結(jié)束了,主要是利用了2.3版本之后的overScrollBy()方法(如果要兼容2.3之前版本需要童靴們自己去實(shí)現(xiàn)相關(guān)邏輯);其次充分的利用了ImageView的ScaleType屬性來(lái)模擬了QQ空間圖片阻尼回彈的效果。再次感謝收看(*^__^*) ……

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Android鬧鈴服務(wù)AlarmManager用法深入分析

    Android鬧鈴服務(wù)AlarmManager用法深入分析

    這篇文章主要介紹了Android鬧鈴服務(wù)AlarmManager用法,結(jié)合實(shí)例形式深入分析了鬧鈴服務(wù)AlarmManager的功能、原理、定義與使用方法,需要的朋友可以參考下
    2016-08-08
  • Android實(shí)現(xiàn)多線程下載文件的方法

    Android實(shí)現(xiàn)多線程下載文件的方法

    這篇文章主要介紹了Android實(shí)現(xiàn)多線程下載文件的方法,以實(shí)例形式較為詳細(xì)的分析了Android多線程文件傳輸及合并等操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-10-10
  • Android Flutter實(shí)現(xiàn)點(diǎn)贊效果的示例代碼

    Android Flutter實(shí)現(xiàn)點(diǎn)贊效果的示例代碼

    點(diǎn)贊這個(gè)動(dòng)作不得不說(shuō)在社交、短視頻等App中實(shí)在是太常見(jiàn)了。本文將利用Flutter制作出一個(gè)點(diǎn)贊動(dòng)畫(huà)效果,感興趣的小伙伴可以學(xué)習(xí)一下
    2022-04-04
  • Android設(shè)置全屏代碼分享

    Android設(shè)置全屏代碼分享

    本文是安卓代碼分享的第一篇,給大家分享了一段簡(jiǎn)單的設(shè)置安卓全屏的代碼,后續(xù)還會(huì)為大家分享一些。
    2014-10-10
  • Android APP瘦身(清除工程中沒(méi)用到的資源)詳解

    Android APP瘦身(清除工程中沒(méi)用到的資源)詳解

    這篇文章主要介紹了Android 清除工程中沒(méi)用到的資源詳解的相關(guān)資料,這里舉例說(shuō)明如何實(shí)現(xiàn),需要的朋友可以參考下
    2016-11-11
  • Android 自定義密碼輸入框?qū)崿F(xiàn)代碼

    Android 自定義密碼輸入框?qū)崿F(xiàn)代碼

    最近做個(gè)項(xiàng)目自定義密碼輸入框功能,下面小編把實(shí)現(xiàn)思路分享到腳本之家平臺(tái),需要的朋友參考下吧
    2018-03-03
  • Android系統(tǒng)添加Linux驅(qū)動(dòng)

    Android系統(tǒng)添加Linux驅(qū)動(dòng)

    今天小編就為大家分享一篇關(guān)于Android系統(tǒng)添加Linux驅(qū)動(dòng)的文章,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-10-10
  • Android使用vcard文件的方法簡(jiǎn)單實(shí)例

    Android使用vcard文件的方法簡(jiǎn)單實(shí)例

    這篇文章主要介紹了Android使用vcard文件的方法,結(jié)合實(shí)例形式分析了Android針對(duì)vcard文件的打開(kāi)、讀取、寫(xiě)入等相關(guān)操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2016-06-06
  • Android 應(yīng)用指定瀏覽器開(kāi)發(fā)實(shí)例

    Android 應(yīng)用指定瀏覽器開(kāi)發(fā)實(shí)例

    這篇文章主要介紹了Android 應(yīng)用指定瀏覽器開(kāi)發(fā)實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • android ToolBar的簡(jiǎn)單使用

    android ToolBar的簡(jiǎn)單使用

    這篇文章主要為大家詳細(xì)介紹了android ToolBar的簡(jiǎn)單使用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12

最新評(píng)論