Android ListView頭部視差控件
效果展示
代碼實現
靜態佈局,爲ListView增加頭部的View
mListView = (ParallaxListView) findViewById(R.id.listview);
mHeadView = View.inflate(this, R.layout.head, null); //異步解析xml中的佈局
mListView.addHeaderView(mHeadView);
自定義ListView,重寫overScrollBy方法
overScrollBy方法會在ListView滑動到頂部和底部時會調用。
獲取頭部控件的大小需要在佈局解析完成後才能知道,否則得到的將是0,
通過設置監聽器mHeadView.getViewTreeObserver().addOnGlobalLayoutListener,
當佈局文件解析完成後,會調用此監聽器中的回調方法,這是就可以將頭部控件傳入自定義的ListView中了
public class ParallaxListView extends ListView {
public ParallaxListView(Context context) {
super(context);
}
public ParallaxListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ParallaxListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private ImageView parallaxImageView;
private int maxHeight;
private int originalHeight;
public void setParallaxImageView(ImageView parallaxImageView) {
this.parallaxImageView = parallaxImageView;
originalHeight = parallaxImageView.getHeight();
//獲得imagview上圖片的原始高度,即爲imageview的最高度
maxHeight = parallaxImageView.getDrawable().getIntrinsicHeight();
}
/**
* 當ListView被滑動到頂部和底部時會調用此方法
*
* @param deltaY y方向滑動的距離。 正:底部到頭;負:頂部到頭
* @param maxOverScrollY 到頭後,最大可滾動的範圍
* @param isTouchEvent 是否是觸摸滑動。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) {
if (deltaY < 0 && isTouchEvent) {
int newHeight = parallaxImageView.getHeight() - deltaY / 3; //新的高度增加與手指拖動的距離不成正比,
//達到拖動吃力的效果
if (newHeight > maxHeight) newHeight = maxHeight;
if (parallaxImageView != null) {
parallaxImageView.getLayoutParams().height = newHeight;
parallaxImageView.requestLayout(); //當完成高度設置後,需要調用重新佈局方法
}
}
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
}
在MainActivity
中
final ImageView parallaxImageView = (ImageView) mHeadView.findViewById(R.id.imageView);
//當從xml中加載完成後,才能知道imageview的長高
mHeadView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@SuppressLint("NewApi")
@Override
public void onGlobalLayout() {
mListView.setParallaxImageView(parallaxImageView);
mHeadView.getViewTreeObserver().removeOnGlobalLayoutListener(this); //取消當前的觀察者
}
});
mAdapter = new MyAdapter(this, R.layout.list_item, lists);
mListView.setAdapter(mAdapter);
設置動畫
當手指擡起時,希望頭部View可以慢慢地回到原來的大小。
爲達到此目的,可以先自定義Animation ,在構造方法中傳入需要動畫效果的View,覆寫applyTransformation方法,
該方法會傳入interpolatedTime參數,表示當前動畫進行的時間百分比,據此可以設置每一幀View的屬性,達到動畫的效果。
public class ResetHeightAnimation extends Animation {
int mOriginalHeight, mTargetHeight;
int totalValue;
View mView;
public ResetHeightAnimation(View view, int targetHeight) {
super();
mTargetHeight = targetHeight;
mView = view;
mOriginalHeight = mView.getHeight();
totalValue = mTargetHeight - mOriginalHeight;
setDuration(400);
setInterpolator(new OvershootInterpolator()); //設置加速器
}
/**
* @param interpolatedTime 標示動畫執行的精度或者百分比
* 範圍在0~1,對於每一個進度,我都可以在此方法中爲制定的view設置屬性,
* 達到動畫額效果
*/
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
int newHeight = (int) (mOriginalHeight + totalValue * interpolatedTime);
mView.getLayoutParams().height = newHeight;
mView.requestLayout();
}
}