android仿QQ個(gè)人主頁(yè)下拉回彈效果
本文實(shí)例為大家分享了android仿QQ個(gè)人主頁(yè)下拉回彈效果的具體代碼,供大家參考,具體內(nèi)容如下
先看效果:
效果不錯(cuò)吧!
進(jìn)入主題之前,先了解ImageView的scaleType的center_crop,網(wǎng)絡(luò)上說(shuō)的已經(jīng)很清楚了 : 以下抄自網(wǎng)絡(luò):
1.Android:scaleType=”centerCrop”
以填滿整個(gè)ImageView為目的,將原圖的中心對(duì)準(zhǔn)ImageView的中心,等比例放大原圖,直到填滿ImageView為止(指的是ImageView的寬和高都要填滿),原圖超過(guò)ImageView的部分作裁剪處理。
均衡的縮放圖像(保持圖像原始比例),使圖片的兩個(gè)坐標(biāo)(寬、高)都大于等于 相應(yīng)的視圖坐標(biāo)(負(fù)的內(nèi)邊距)。圖像則位于視圖的中央。 在XML 中可以使用的語(yǔ)法:android:scaleType=”centerCrop”。
不說(shuō)廢話,直接進(jìn)入主題??!
思路
1.先將topView的布局和listview平級(jí),然后將topview以及topview包裹的imageView中傳listview,即一般是activity的layout
2.重寫(xiě)listView的ontoucEvent()方法,但不做任何攔截,只在action時(shí),控制imageView以及topView的高度,使其重新layout然后重新布局就可以了。
3.以上是大概思路,這里具體分析:當(dāng)action_down時(shí)記錄其初始位置,action_move時(shí)得到dy,通過(guò)dy來(lái)判斷是上啦還是下拉:
(1)dy>0,則是下拉,不斷重新設(shè)置topView和imageView的高度,又因?yàn)閕mageView的scaleType=center_crop,所以圖片會(huì)按照這個(gè)規(guī)則進(jìn)行等比拉伸,當(dāng)?shù)竭_(dá)圖片最大時(shí)就會(huì)有不斷放大的過(guò)程
當(dāng)松開(kāi)手或者手指移出屏幕外時(shí)(action_up|action_outside|action_cancel)時(shí)讓其回到初始位置,并伴著回彈過(guò)程,這里通過(guò)自定義動(dòng)畫(huà)讓其具備回彈效果
(2)dy<0,則是上拉,上推的過(guò)程,由于topView和Imageview不具備滾動(dòng)的效果,所以上推也是通過(guò)控制topView和ImageView的高度,并且當(dāng)TopVIew和ImageView滑出屏幕時(shí)就不在更改高度防止不斷的繪制,提高性能。
ok,大體思路就這樣。具體分析代碼如下:
實(shí)現(xiàn):
activity的xml
stretch_act.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!--這是topView--> <RelativeLayout android:id="@+id/rl_top" android:layout_width="match_parent" android:layout_height="wrap_content" > <!--這是imageView,一定要設(shè)置scaleType為centerCrop--> <ImageView android:id="@+id/iv_stretch_pic" android:layout_width="match_parent" android:layout_height="wrap_content" android:scaleType="centerCrop" android:src="@drawable/stretch_s" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@id/iv_stretch_pic" android:text="你最美,你最酷…………^^" android:textSize="16sp"/> </RelativeLayout> <!--這是自定義的listview--> <com.example.zwr.myapplication.widget.StretchListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="match_parent" android:cacheColorHint="@null" android:divider="@null" android:listSelector="#00000000"/> </LinearLayout>
看看StretchListView:
/*** * @param topView * @param imgResId 圖片id */ public void setTopView(View topView, int imgResId) { if (null != topView) { this.topView = topView; imageView = (ImageView) topView.findViewById(imgResId); } }
通過(guò)這個(gè)對(duì)外的方法,將topView以及ImageView的id傳進(jìn)來(lái)
分析:重新ListView的onTouchEvent():
ACTION_DOWN: case MotionEvent.ACTION_DOWN: startY = ev.getRawY(); if (!hadInit) {//初始化,只要初始化一次就夠了 childAt0Top = getChildAt(0).getTop(); ivInitHeight = imageView.getHeight(); hadInit = true; } break;
只是進(jìn)行一些初始化操作:
1. startY:相對(duì)于屏幕頂部的高度
2. childAt0Top,獲取listview的第一個(gè)view的top距離、
3. ivInitHeight:獲取ImageView的初始高度,即剛進(jìn)來(lái)時(shí)的高度
ACTION_MOVE:
case MotionEvent.ACTION_MOVE: Log.d(TAG, "dy = " + dy); dy = ev.getRawY() - startY; if (dy > 0 && 0 == getFirstVisiblePosition() && childAt0Top == getChildAt(0).getTop()) {//(1)手指從上往下拉:下拉 int tempDy = (int) (dy + 0.5); //一定也要給topView增加一定的高度,否則從上啦到下拉就不會(huì)顯示 imageView.getLayoutParams().height = imageView.getHeight() + tempDy; topView.getLayoutParams().height = topView.getHeight() + tempDy; topView.requestLayout(); isChangedHeight = true; } else {//(2)手指從下往上拉:上拉 int tempDy = (int) (dy - 0.5); int currHeight = imageView.getHeight(); float translationY = getNegativeMaxValue(tempDy, -currHeight, 0); if (translationY <= 0 && currHeight > 0) { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) topView.getLayoutParams(); //一定要減去titleBar,如果沒(méi)有去掉Winow.xxx.Title,還要減去這個(gè)高度,否則會(huì)顯示不全 lp.height = topView.getHeight() + (int) translationY; topView.requestLayout();// isChangedHeight = true; } } //用這個(gè)getRawY而不是用getY,是因?yàn)閘istview也會(huì)隨著改變, //而getY獲取的就是listview本身的Y,所以基本是變化不大的, // 而使用getRawY相對(duì)于屏幕的距離,保證滑動(dòng)了多大的距離就改變多大的距離 startY = ev.getRawY(); break;
當(dāng)下拉時(shí):主要條件如下:
1.dy > 0 && 0 == getFirstVisiblePosition() && childAt0Top ==
getChildAt(0).getTop()
意思是當(dāng)下拉時(shí),并且listview的第一個(gè)位置顯示全了,才能下拉放大圖片,這是避免,listview已經(jīng)發(fā)生滾動(dòng)了,需要回到初始位置才能下拉放大,否則會(huì)出現(xiàn),立即下拉放大,體驗(yàn)不好
2.當(dāng)上拉時(shí) 主要條件
if (translationY <= 0 && currHeight > 0)
currHeight>0:當(dāng)前ImageView的高度,如果已經(jīng)滾動(dòng)到頂部或者超出,則不再進(jìn)行滾動(dòng),防止已經(jīng)滾出屏幕不可視了,還在進(jìn)行滾動(dòng)。
translationY<=0: 這個(gè)值是滾動(dòng)的距離,這個(gè)距離不能超過(guò)ImageView的高度,由于上拉時(shí)dy是負(fù)值,所以要判斷是否小于0;其主要方法如下:
float translationY = getNegativeMaxValue(tempDy, -currHeight, 0); /*** * 手指上移過(guò)程dy是負(fù)數(shù) * 返回負(fù)數(shù)最大值:0是最大值,不可以超過(guò) * * @param value 移動(dòng)的最終距離:上次的位置+當(dāng)次移動(dòng)的偏移量之和,就是本次要移動(dòng)的最終的偏移量 * @param canMoveMaxValue 可移動(dòng)的最大值 * @param maxValue * @return */ public static float getNegativeMaxValue(float value,float canMoveMaxValue, float maxValue) { return Math.min(maxValue, Math.max(canMoveMaxValue, value)); }
ACTION_UP:
case MotionEvent.ACTION_OUTSIDE: case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (isChangedHeight) { if (imageView.getHeight() > ivInitHeight) {// (1)手指從上往下拉:下拉 ResetAnimation resetAnimation = new ResetAnimation(ivInitHeight, imageView, topView); resetAnimation.setDuration(200); imageView.startAnimation(resetAnimation); } else {//(2)手指從下往上拉:上拉。。。這個(gè)不用處理。。。因?yàn)樯侠笏砷_(kāi)讓其topview固定 } isChangedHeight = false; } break;
isChangedHeight:當(dāng)發(fā)生ImageView發(fā)生改變,并且是下拉時(shí),這是松開(kāi)手或者手指移出屏幕,則讓其回彈到初始位置;這里是通過(guò)自定義動(dòng)畫(huà)來(lái)改變其變化的高度,達(dá)到回彈效果 代碼如下
/** * 自定義回彈動(dòng)畫(huà),使imageView和topView過(guò)渡回彈到初始位置 */ static class ResetAnimation extends Animation { private View topView; private int topCurrHeight; private ImageView ivStretch; private int ivInitHeight; private int ivCurrHeight; public ResetAnimation(int ivInitHeiht, ImageView ivStretch, View topView) { this.ivInitHeight = ivInitHeiht; this.ivCurrHeight = ivStretch.getHeight(); this.topCurrHeight = topView.getHeight(); this.ivStretch = ivStretch; this.topView = topView; } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { int dy = (int) ((ivCurrHeight - ivInitHeight) * interpolatedTime); Log.d(TAG, "anim dy = " + dy); ivStretch.getLayoutParams().height = ivCurrHeight - dy; topView.getLayoutParams().height = topCurrHeight - dy; topView.requestLayout(); } }
其實(shí)主要是applyTransformation(float interpolatedTime, Transformation t) 這個(gè)方法
主要是通過(guò)這個(gè)漸變因子interpolatedTime來(lái)控制,其值范圍是(0~1) 所以計(jì)算漸變的高度如下
int dy = (int) ((ivCurrHeight - ivInitHeight) * interpolatedTime);
然后一定要記得調(diào)用topView.requestLayout(),讓其重新布局繪制。這樣就完成了,所有代碼,也就一百行代碼左右,是不是很簡(jiǎn)單。而且通過(guò)這個(gè)demo,可以很好的拓展到scrollview中。
注意:
網(wǎng)上有些demo是通過(guò)overScrollBy()這個(gè)方法中搞事情,因?yàn)?/p>
/*** * 這個(gè)方法是在滑出屏幕時(shí)回調(diào),但是由于android系統(tǒng)國(guó)內(nèi)廠商修改的面目全非,有些機(jī)型是不會(huì)回調(diào)的,比如vivo * 所以不要使用這個(gè)方法搞事情 * * @param scrollX * @param scrollY */ @Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { Log.d(TAG, "deltax = " + deltaX + " deltaY = " + deltaY); return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); }
其自帶dy,還有一些其它的參數(shù),應(yīng)有盡有。但是由于android系統(tǒng)是開(kāi)源的導(dǎo)致有些系統(tǒng)是無(wú)法回調(diào)這個(gè)方法的,以至于無(wú)法實(shí)現(xiàn)回彈效果(比如:vivoX5)等等。所以在onTouchEvent()搞事情才是王道
ok!,以上有什么問(wèn)題,請(qǐng)不吝指正?。?!
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android ScrollView的頂部下拉和底部上拉回彈效果
- Android RecyclerView上拉加載更多功能回彈實(shí)現(xiàn)代碼
- Android界面上拉下拉的回彈效果實(shí)例代碼
- Android ReboundScrollView仿IOS拖拽回彈效果
- Android仿IOS回彈效果 支持任何控件
- Android ScrollView實(shí)現(xiàn)橫向和豎向拖動(dòng)回彈效果
- Android自定義ScrollView實(shí)現(xiàn)放大回彈效果
- Android編程ViewPager回彈效果實(shí)例分析
- Android自定義控件仿ios下拉回彈效果
- Android基于reclyview實(shí)現(xiàn)列表回彈動(dòng)畫(huà)效果
相關(guān)文章
Android控件RefreshableView實(shí)現(xiàn)下拉刷新
這篇文章主要為大家詳細(xì)介紹了Android控件RefreshableView實(shí)現(xiàn)下拉刷新,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11TextView中URL等指定特殊字符串與點(diǎn)擊事件解析
這篇文章主要為大家詳細(xì)介紹了TextView中URL等指定特殊字符串與點(diǎn)擊事件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11Android三方依賴沖突Gradle中exclude的使用
這篇文章主要介紹了Android三方依賴沖突Gradle中exclude的使用,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09Android實(shí)現(xiàn)通訊錄效果——獲取手機(jī)號(hào)碼和姓名
這篇文章主要介紹了Android實(shí)現(xiàn)通訊錄效果——獲取手機(jī)號(hào)碼和姓名的相關(guān)資料,需要的朋友可以參考下2016-03-03Android中AndroidStudio&Kotlin安裝到運(yùn)行過(guò)程及常見(jiàn)問(wèn)題匯總
這篇文章主要介紹了Android(AndroidStudio&Kotlin)安裝到運(yùn)行過(guò)程及常見(jiàn)問(wèn)題匯總,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒借鑒價(jià)值,需要的朋友可以參考下2020-03-03Android Button按鈕點(diǎn)擊背景和文字變化操作
這篇文章主要介紹了Android Button按鈕點(diǎn)擊背景和文字變化操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08Android實(shí)現(xiàn)系統(tǒng)日歷同步日程
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)系統(tǒng)日歷同步日程,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04Android實(shí)現(xiàn)監(jiān)聽(tīng)電話呼叫狀態(tài)的方法
這篇文章主要介紹了Android實(shí)現(xiàn)監(jiān)聽(tīng)電話呼叫狀態(tài)的方法,涉及Android權(quán)限控制及電話狀態(tài)監(jiān)聽(tīng)的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10android 自定義view實(shí)現(xiàn)彩虹進(jìn)度條功能
實(shí)現(xiàn)一個(gè)彩虹色進(jìn)度條功能,不說(shuō)明具體用途大家應(yīng)該能猜到,想找別人造的輪子,但是沒(méi)有合適的,所以決定自己實(shí)現(xiàn)一個(gè),下面小編通過(guò)實(shí)例代碼給大家分享android 自定義view實(shí)現(xiàn)彩虹進(jìn)度條功能,感興趣的朋友一起看看吧2024-06-06