Android通過overScrollBy實(shí)現(xiàn)下拉視差特效
overScrollBy實(shí)現(xiàn)下拉視差特效,效果圖如下

先來分析overScrollBy方法的使用,它是View的方法,參數(shù)有點(diǎn)多:
/**
* 當(dāng)滑動(dòng)的超出上,下,左,右最大范圍時(shí)回調(diào)
*
* @param deltaX x方向的瞬時(shí)偏移量,左邊到頭,向右拉為負(fù),右邊到頭,向左拉為正
* @param deltaY y方向的瞬時(shí)偏移量,頂部到頭,向下拉為負(fù),底部到頭,向上拉為正
* @param scrollX 水平方向的永久偏移量
* @param scrollY 豎直方向的永久偏移量
* @param scrollRangeX 水平方向滑動(dòng)的范圍
* @param scrollRangeY 豎直方向滑動(dòng)的范圍
* @param maxOverScrollX 水平方向最大滑動(dòng)范圍
* @param maxOverScrollY 豎直方向最大滑動(dòng)范圍
* @param isTouchEvent 是否是手指觸摸滑動(dòng), true為手指, false為慣性
* @return
*/
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY, int maxOverScrollX,
int maxOverScrollY, boolean isTouchEvent) {
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY,
scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY,
isTouchEvent);
}
大致步驟如下:
1.這整體是一個(gè)ListView,所以需要自定義一個(gè)ListView.
2.處理頭部布局文件,將其以HeaderView的方式添加到自定義的ListView中
3.需要獲取HeaderView的ImageView的初始高度和ImageView中圖片的高度.因?yàn)檫@2個(gè)高度將決定下來的時(shí)候圖片拉出的范圍,以及松手后圖片回彈的動(dòng)畫效果.對(duì)應(yīng)控件寬高的獲取,有興趣的可以看這篇文章淺談自定義View的寬高獲取
4.在overScrollBy方法內(nèi)通過修改ImageView的LayoutParams的height值來顯示更多的圖片內(nèi)容.
5.在onTouchEvent方法內(nèi)處理ACTION_UP事件,使ImageView有回彈的動(dòng)畫效果,這里介紹2種方式,分別是屬性動(dòng)畫和自定義動(dòng)畫.
好了,先來看HeaderView的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="160dp"
<span style="color:#ff0000;">android:scaleType="centerCrop"</span>
android:src="@drawable/header" />
</LinearLayout>
沒什么特別的,就是一個(gè)ImageView,通過src設(shè)置了一張圖片,這里唯一要將的就是scaleType屬性,我這邊設(shè)置了centerCrop,以圖片的最小的邊開始截取,因?yàn)檫@里選擇的圖片是高度大于寬度的,所以裁剪的時(shí)候會(huì)保留完整的寬度,中心裁剪,如下圖所示:

自定義ListView代碼,整體代碼還是比較簡(jiǎn)短的.
/**
* Created by mChenys on 2015/12/23.
*/
public class MyListView extends ListView {
private ImageView mHeaderIv; //HeaderView 的ImageView
private int mOriginalHeight; //最初ImageView的高度
private int mDrawableHeight;//ImageView中圖片的高度
public MyListView(Context context) {
this(context, null);
}
public MyListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* 設(shè)置頭部和獲取高度信息
*/
private void init() {
//初始化頭部文件
View headerView = View.inflate(getContext(), R.layout.view_header, null);
mHeaderIv = (ImageView) headerView.findViewById(R.id.imageView);
//將其添加到ListView的頭部
addHeaderView(headerView);
//通過設(shè)置監(jiān)聽來獲取控件的高度
mHeaderIv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onGlobalLayout() {
//只需監(jiān)聽一次,否則之后的onLayout方法回調(diào)的時(shí)候還是會(huì)回調(diào)這里
mHeaderIv.getViewTreeObserver().removeOnGlobalLayoutListener(this);
mOriginalHeight = mHeaderIv.getMeasuredHeight();//獲取ImageView的初始高度
mDrawableHeight = mHeaderIv.getDrawable().getIntrinsicHeight();//獲取ImageView中圖片的高度
}
});
//去掉下拉到頭部后的藍(lán)色線
setOverScrollMode(OVER_SCROLL_NEVER);
}
/**
* 當(dāng)滑動(dòng)的超出上,下,左,右最大范圍時(shí)回調(diào)
*
* @param deltaX x方向的瞬時(shí)偏移量,左邊到頭,向右拉為負(fù),右邊到頭,向左拉為正
* @param deltaY y方向的瞬時(shí)偏移量,頂部到頭,向下拉為負(fù),底部到頭,向上拉為正
* @param scrollX 水平方向的永久偏移量
* @param scrollY 豎直方向的永久偏移量
* @param scrollRangeX 水平方向滑動(dòng)的范圍
* @param scrollRangeY 豎直方向滑動(dòng)的范圍
* @param maxOverScrollX 水平方向最大滑動(dòng)范圍
* @param maxOverScrollY 豎直方向最大滑動(dòng)范圍
* @param isTouchEvent 是否是手指觸摸滑動(dòng), true為手指, false為慣性
* @return
*/
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY, int maxOverScrollX,
int maxOverScrollY, boolean isTouchEvent) {
// 手指拉動(dòng)并且是下拉
if (isTouchEvent && deltaY < 0) {
// 把拉動(dòng)的瞬時(shí)變化量的絕對(duì)值交給Header, 就可以實(shí)現(xiàn)放大效果
if (mHeaderIv.getHeight() <= mDrawableHeight) {
// 高度不超出圖片最大高度時(shí),才讓其生效
int newHeight = (int) (mHeaderIv.getHeight() + Math.abs(deltaY / 3.0f));//這里除以3是為了達(dá)到視差的效果
mHeaderIv.getLayoutParams().height = newHeight;
//此方法必須調(diào)用,調(diào)用后會(huì)重新調(diào)用onMeasure和onLayout方法進(jìn)行測(cè)量和定位
mHeaderIv.requestLayout();
}
}
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
// 執(zhí)行回彈動(dòng)畫, 方式一: 屬性動(dòng)畫\值動(dòng)畫
//獲取ImageView在松手時(shí)的高度
int currHeight = mHeaderIv.getHeight();
// 從當(dāng)前高度mHeaderIv.getHeight(), 執(zhí)行動(dòng)畫到原始高度mOriginalHeight
ValueAnimator animator = ValueAnimator.ofInt(currHeight, mOriginalHeight);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
mHeaderIv.getLayoutParams().height = value;
//此方法必須調(diào)用,調(diào)用后會(huì)重新調(diào)用onMeasure和onLayout方法進(jìn)行測(cè)量和定位
mHeaderIv.requestLayout();
}
});
animator.setDuration(500);
animator.setInterpolator(new OvershootInterpolator());
animator.start();
//方式二,通過自定義動(dòng)畫
/*ResetAnimation animation = new ResetAnimation(mHeaderIv, mHeaderIv.getHeight(), mOriginalHeight);
startAnimation(animation);*/
break;
}
return super.onTouchEvent(ev);
}
}
看看自定義動(dòng)畫:
/**
* 自定義動(dòng)畫
* Created by mChenys on 2015/12/24.
*/
public class ResetAnimation extends Animation {
private final ImageView headerIv; //要執(zhí)行動(dòng)畫的目標(biāo)ImageView
private final int startHeight;//執(zhí)行動(dòng)畫的開始時(shí)的高度
private final int endHeight;//執(zhí)行動(dòng)畫結(jié)束時(shí)的高度
private IntEvaluator mEvaluator; //整型估值器
/**
* 構(gòu)造方法初始化
*
* @param headerIv 應(yīng)用動(dòng)畫的目標(biāo)控件
* @param startHeight 開始的高度
* @param endHeight 結(jié)束的高度
*/
public ResetAnimation(ImageView headerIv, int startHeight, int endHeight) {
this.headerIv = headerIv;
this.startHeight = startHeight;
this.endHeight = endHeight;
//定義一個(gè)int類型的類型估值器,用于獲取實(shí)時(shí)變化的高度值
mEvaluator = new IntEvaluator();
//設(shè)置動(dòng)畫持續(xù)時(shí)間
setDuration(500);
//設(shè)置插值器
setInterpolator(new OvershootInterpolator());
}
/**
* 在指定的時(shí)間內(nèi)一直執(zhí)行該方法,直到動(dòng)畫結(jié)束
* interpolatedTime:0-1 標(biāo)識(shí)動(dòng)畫執(zhí)行的進(jìn)度或者百分比
*
* @param interpolatedTime
* @param t
*/
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
int currHeight = mEvaluator.evaluate(interpolatedTime, startHeight, endHeight);
//通過LayoutParams不斷的改變其高度
headerIv.getLayoutParams().height = currHeight;
//此方法必須調(diào)用,調(diào)用后會(huì)重新調(diào)用onMeasure和onLayout方法進(jìn)行測(cè)量和定位
headerIv.requestLayout();
}
}
MainActivity測(cè)試類:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyListView listView = new MyListView(this);
listView.setDividerHeight(1);
listView.setSelector(new ColorDrawable());
listView.setCacheColorHint(Color.TRANSPARENT);
listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, Cheeses.NAMES));
setContentView(listView);
}
}
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android開發(fā)使用json實(shí)現(xiàn)服務(wù)器與客戶端數(shù)據(jù)的交互功能示例
這篇文章主要介紹了Android開發(fā)使用json實(shí)現(xiàn)服務(wù)器與客戶端數(shù)據(jù)的交互功能,結(jié)合具體實(shí)例形式分析了Android使用json格式數(shù)據(jù)在服務(wù)器與客戶端傳遞實(shí)現(xiàn)數(shù)據(jù)庫查詢功能的步驟與相關(guān)操作技巧,需要的朋友可以參考下2017-10-10
android 如何獲取MCC/MNC控制小區(qū)廣播的開啟
獲取MCC/MNC以便控制小區(qū)廣播的開啟下面針對(duì)于單卡、雙卡,為大家詳細(xì)介紹下具體的實(shí)現(xiàn),感興趣的朋友可以參考下哈2013-06-06
Android?實(shí)現(xiàn)自定義圓形進(jìn)度條的三種常用方法
這篇文章主要介紹了Android?實(shí)現(xiàn)自定義圓形進(jìn)度條的三種常用方法的相關(guān)資料,需要的朋友可以參考下2023-03-03
詳解Android?Flutter中SliverAppBar的使用教程
對(duì)于一個(gè)APP來說,肯定會(huì)有一個(gè)AppBar,這個(gè)AppBar一般包含了APP的導(dǎo)航信息等。在lutter已經(jīng)為我們提供了一個(gè)非常強(qiáng)大的AppBar組件,這個(gè)組件叫做SliverAppBar。本文就來聊聊它的具體使用吧2023-01-01
Android使用DisplayManager創(chuàng)建虛擬屏流程及原理解析
這篇文章主要介紹了Android使用DisplayManager創(chuàng)建虛擬屏流程及原理解析,DisplayManager提供了createVirtualDisplay接口,用于創(chuàng)建虛擬屏,虛擬屏可以把屏幕分出不同的區(qū)域,需要的朋友可以參考下2024-05-05
Android控件Spinner實(shí)現(xiàn)下拉列表及監(jiān)聽功能
這篇文章主要介紹了Android控件Spinner實(shí)現(xiàn)下拉列表及監(jiān)聽功能,這是在Web開發(fā)中一個(gè)必不可少的交互性組件,而在Android中的對(duì)應(yīng)實(shí)現(xiàn)就是Spinner。需要的朋友可以參考下2018-07-07
React Native學(xué)習(xí)之Android的返回鍵BackAndroid詳解
這篇文章主要給大家介紹了關(guān)于React Native學(xué)習(xí)之Android的返回鍵BackAndroid的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用React Native具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起看看吧。2017-10-10
Android使用OKHttp庫實(shí)現(xiàn)視頻文件的上傳到服務(wù)器功能
這篇文章主要介紹了Android使用OKHttp庫實(shí)現(xiàn)視頻文件的上傳到服務(wù)器功能,需要的朋友可以參考下2018-03-03
Android實(shí)現(xiàn)的可以調(diào)整透明度的圖片查看器實(shí)例
這篇文章主要介紹了Android實(shí)現(xiàn)的可以調(diào)整透明度的圖片查看器,需要的朋友可以參考下2014-07-07

