android ScrollView實(shí)現(xiàn)下拉放大頭部圖片
前言
之前做項(xiàng)目的時(shí)候,需要實(shí)現(xiàn)類(lèi)似微博個(gè)人主頁(yè)的ScrollView效果,就是到頂部時(shí)繼續(xù)下拉會(huì)放大頂部的圖片。然后在網(wǎng)上找了一篇相關(guān)的實(shí)現(xiàn),效果非常好,代碼也很簡(jiǎn)潔易懂。(傳送門(mén): 自定義scrollView實(shí)現(xiàn)頂部圖片下拉放大),那么我這里就只是在其基礎(chǔ)上修改了一點(diǎn)點(diǎn)而已,比如在代碼中控制圖片居中、增加動(dòng)態(tài)設(shè)置放大的控件、使用自定義的最大放大倍數(shù)等,都是很簡(jiǎn)單的修改,還添加了滑動(dòng)的監(jiān)聽(tīng)回調(diào)(項(xiàng)目需要)。
效果如下:

思路
老樣子,我們先來(lái)說(shuō)下思路,比起代碼,思路才是最重要的。具體步驟如下:
1. 獲得要放大的控件,并獲得其寬高;
2. 在頂部時(shí)繼續(xù)往下拉,通過(guò)LayoutParams改變控件的寬高;
3. 手指抬起時(shí)初始化各項(xiàng)參數(shù),通過(guò)屬性動(dòng)畫(huà)回彈控件。
實(shí)現(xiàn)
直接看代碼
public class HeadZoomScrollView extends ScrollView {
public HeadZoomScrollView(Context context) {
super(context);
}
public HeadZoomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HeadZoomScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
// 用于記錄下拉位置
private float y = 0f;
// zoomView原本的寬高
private int zoomViewWidth = 0;
private int zoomViewHeight = 0;
// 是否正在放大
private boolean mScaling = false;
// 放大的view,默認(rèn)為第一個(gè)子view
private View zoomView;
public void setZoomView(View zoomView) {
this.zoomView = zoomView;
}
// 滑動(dòng)放大系數(shù),系數(shù)越大,滑動(dòng)時(shí)放大程度越大
private float mScaleRatio = 0.4f;
public void setmScaleRatio(float mScaleRatio) {
this.mScaleRatio = mScaleRatio;
}
// 最大的放大倍數(shù)
private float mScaleTimes = 2f;
public void setmScaleTimes(int mScaleTimes) {
this.mScaleTimes = mScaleTimes;
}
// 回彈時(shí)間系數(shù),系數(shù)越小,回彈越快
private float mReplyRatio = 0.5f;
public void setmReplyRatio(float mReplyRatio) {
this.mReplyRatio = mReplyRatio;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// 不可過(guò)度滾動(dòng),否則上移后下拉會(huì)出現(xiàn)部分空白的情況
setOverScrollMode(OVER_SCROLL_NEVER);
// 獲得默認(rèn)第一個(gè)view
if (getChildAt(0) != null && getChildAt(0) instanceof ViewGroup && zoomView == null) {
ViewGroup vg = (ViewGroup) getChildAt(0);
if (vg.getChildCount() > 0) {
zoomView = vg.getChildAt(0);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (zoomViewWidth <= 0 || zoomViewHeight <=0) {
zoomViewWidth = zoomView.getMeasuredWidth();
zoomViewHeight = zoomView.getMeasuredHeight();
}
if (zoomView == null || zoomViewWidth <= 0 || zoomViewHeight <= 0) {
return super.onTouchEvent(ev);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
if (!mScaling) {
if (getScrollY() == 0) {
y = ev.getY();//滑動(dòng)到頂部時(shí),記錄位置
} else {
break;
}
}
int distance = (int) ((ev.getY() - y)*mScaleRatio);
if (distance < 0) break;//若往下滑動(dòng)
mScaling = true;
setZoom(distance);
return true;
case MotionEvent.ACTION_UP:
mScaling = false;
replyView();
break;
}
return super.onTouchEvent(ev);
}
/**放大view*/
private void setZoom(float s) {
float scaleTimes = (float) ((zoomViewWidth+s)/(zoomViewWidth*1.0));
// 如超過(guò)最大放大倍數(shù),直接返回
if (scaleTimes > mScaleTimes) return;
ViewGroup.LayoutParams layoutParams = zoomView.getLayoutParams();
layoutParams.width = (int) (zoomViewWidth + s);
layoutParams.height = (int)(zoomViewHeight*((zoomViewWidth+s)/zoomViewWidth));
// 設(shè)置控件水平居中
((MarginLayoutParams) layoutParams).setMargins(-(layoutParams.width - zoomViewWidth) / 2, 0, 0, 0);
zoomView.setLayoutParams(layoutParams);
}
/**回彈*/
private void replyView() {
final float distance = zoomView.getMeasuredWidth() - zoomViewWidth;
// 設(shè)置動(dòng)畫(huà)
ValueAnimator anim = ObjectAnimator.ofFloat(distance, 0.0F).setDuration((long) (distance * mReplyRatio));
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setZoom((Float) animation.getAnimatedValue());
}
});
anim.start();
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (onScrollListener!=null) onScrollListener.onScroll(l,t,oldl,oldt);
}
private OnScrollListener onScrollListener;
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
/**滑動(dòng)監(jiān)聽(tīng)*/
public interface OnScrollListener{
void onScroll(int scrollX,int scrollY,int oldScrollX, int oldScrollY);
}
}
可以看到,在onTouchEvent方法中,先判斷當(dāng)前是否為放大狀態(tài),不是的話就在頂部的時(shí)候記錄觸摸事件的位置,當(dāng)然這個(gè)寫(xiě)在ACTION_DOWN事件中也是可以的,若不在頂部,則不處理。
之后計(jì)算滑動(dòng)的距離,若是往下滑動(dòng),則不處理,需要注意的是這個(gè)距離是指當(dāng)前位置與最開(kāi)始的ACTION_DOWN動(dòng)作的距離,因此當(dāng)這個(gè)距離小于0的時(shí)候,便是”沒(méi)放大&&往下滑”,這個(gè)時(shí)候應(yīng)該滑動(dòng)的是ScrollView,恩,沒(méi)毛病。當(dāng)距離不小于0的時(shí)候,開(kāi)始放大控件,可以看到調(diào)用了setZoom方法,注意,在這里其實(shí)控件的下拉放大、上拉恢復(fù)都做了, 回彈其實(shí)也是調(diào)用這個(gè)方法。
抬手的時(shí)候回彈,這個(gè)不需要說(shuō)了。
代碼整體來(lái)說(shuō)比較簡(jiǎn)單,要是你需要其他實(shí)現(xiàn),也可以很方便地加上,比如我們要放大圖片的時(shí)候像微博一樣,旋轉(zhuǎn)外部的菊花,放手的時(shí)候刷新數(shù)據(jù)怎么辦?你完全可以在
onTouchEvent中增加回調(diào)接口,然后在外部實(shí)現(xiàn)具體邏輯就可以了。
使用
直接像普通的ScollView樣使用就可以了,這個(gè)就不累贅了。
源碼:下載地址
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android自動(dòng)化獲取卡頓信息的實(shí)現(xiàn)方法
自動(dòng)化獲取卡頓信息就像給App裝 “行車(chē)記錄儀” —— 實(shí)時(shí)記錄主線程的“駕駛狀態(tài)”,一旦發(fā)現(xiàn)“急剎車(chē)”(卡頓),立刻保存現(xiàn)場(chǎng)(堆棧),事后回看錄像(日志)精準(zhǔn)定位問(wèn)題,本文給大家介紹了Android自動(dòng)化獲取卡頓信息的實(shí)現(xiàn)方法,需要的朋友可以參考下2025-02-02
Android操作系統(tǒng)的架構(gòu)設(shè)計(jì)分析
這篇文章主要介紹了Android操作系統(tǒng)的架構(gòu)設(shè)計(jì)分析,Android系統(tǒng)架構(gòu)分為L(zhǎng)inux內(nèi)核驅(qū)動(dòng)、C/C ++框架、Java框架、Java應(yīng)用程序,本文分別講解了它的作用,需要的朋友可以參考下2015-06-06
Android畫(huà)板開(kāi)發(fā)之撤銷(xiāo)反撤銷(xiāo)功能
這篇文章主要為大家詳細(xì)介紹了Android畫(huà)板開(kāi)發(fā)之撤銷(xiāo),反撤銷(xiāo)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
簡(jiǎn)單實(shí)現(xiàn)android輪播圖
這篇文章主要為大家詳細(xì)介紹了android輪播圖的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01
Android ListView實(shí)現(xiàn)下拉加載功能
這篇文章主要為大家詳細(xì)介紹了Android ListView實(shí)現(xiàn)下拉加載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Android開(kāi)發(fā)實(shí)現(xiàn)撥打電話與發(fā)送信息的方法分析
這篇文章主要介紹了Android開(kāi)發(fā)實(shí)現(xiàn)撥打電話與發(fā)送信息的方法,結(jié)合實(shí)例形式分析了Android撥打電話及發(fā)送信息相關(guān)布局、功能實(shí)現(xiàn)及權(quán)限控制操作技巧,需要的朋友可以參考下2017-12-12
Android實(shí)現(xiàn)夜間模式切換功能實(shí)現(xiàn)代碼
現(xiàn)在很多App都有夜間模式,特別是閱讀類(lèi)的App,夜間模式現(xiàn)在已經(jīng)是閱讀類(lèi)App的標(biāo)配,本篇文章主要介紹了Android實(shí)現(xiàn)夜間模式功能實(shí)現(xiàn)代碼,有興趣的可以了解一下。2017-03-03
Android 物理游戲之重力系統(tǒng)開(kāi)發(fā)示例代碼
介紹Android 物理游戲之重力系統(tǒng),這里提供了詳細(xì)的資料整理,并附示例代碼和實(shí)現(xiàn)效果圖,有興趣的小伙伴可以參考下2016-08-08
Android獲取當(dāng)前位置的經(jīng)緯度數(shù)據(jù)
這篇文章主要介紹了Android獲取當(dāng)前位置的經(jīng)緯度數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2016-02-02

