快速打造帶有視差效果的ListView

博文出處:快速打造帶有視差效果的ListView,歡迎大家關(guān)注我的博客灿椅,謝謝蝎亚!

在上一篇博文中九孩,我們實現(xiàn)了仿美團(tuán)的下拉刷新。而今天的主題還是與 ListView 有關(guān)发框,這次是來實現(xiàn)具有視差效果的 ListView 躺彬。

那么到底什么是視差效果呢煤墙?一起來看效果圖就知道了:

ListView視差效果圖gif

我們可以看到 ListView 的 HeaderView 會跟隨 ListView 的滑動而變大,HeaderView里的圖片會有縮放效果宪拥。這些可以使用屬性動畫來實現(xiàn)仿野。接下來我們就來動手吧!

首先自定義幾個屬性她君,在之后可以用到:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ZoomListView">
        <!-- headerView的高度 -->
        <attr name="header_height" format="dimension|reference"></attr>
        <!-- headerView的最大高度 -->
        <attr name="header_max_height" format="dimension|reference"></attr>
        <!-- headerView里面的圖片最大的伸縮量 -->
        <attr name="header_max_scale" format="float"></attr>
    </declare-styleable>
</resources>

之后創(chuàng)建 ZoomListView 類脚作,繼承自 ListView :

public class ZoomListView extends ListView {
    
    // 最大的伸縮量
    private final float defaultHeaderMaxScale = 1.2f;
    // 頭部最大的高度
    private float headerMaxHeight;
    // 頭部初始高度
    private float headerHeight;
    // 頭部默認(rèn)初始高度
    private float defaultHeaderHeight;
    // 頭部默認(rèn)最大的高度
    private float defaultHeaderMaxHeight;
    private ImageView headerView;
    private ViewGroup.LayoutParams layoutParams;
    private LinearLayout linearLayout;
    // 最大的縮放值
    private float headerMaxScale;

    public ZoomListView(Context context) {
        this(context, null);
    }

    public ZoomListView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ZoomListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        defaultHeaderHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 160, context.getResources().getDisplayMetrics());
        defaultHeaderMaxHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 240, context.getResources().getDisplayMetrics());
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ZoomListView);
        headerHeight = a.getDimension(R.styleable.ZoomListView_header_height, defaultHeaderHeight);
        headerMaxHeight = a.getDimension(R.styleable.ZoomListView_header_max_height, defaultHeaderMaxHeight);
        headerMaxScale = a.getFloat(R.styleable.ZoomListView_header_max_scale, defaultHeaderMaxScale);
        a.recycle();
        initView();
    }
    ...
}

到這里都是按部就班式的,設(shè)置好自定義屬性的初始值缔刹,之后調(diào)用 initView() 球涛,那就來看看 initView() 方法:

private void initView() {
    headerView = new ImageView(getContext());
    headerView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    linearLayout = new LinearLayout(getContext());
    linearLayout.addView(headerView);
    layoutParams = headerView.getLayoutParams();
    if (layoutParams == null) {
        layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) headerHeight);
    } else {
        layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
        layoutParams.height = (int) headerHeight;
    }
    headerView.setLayoutParams(layoutParams);
    addHeaderView(linearLayout);
}

public void setDrawableId(int id) {
    headerView.setImageResource(id);
}

可以看出在 initView() 里我們創(chuàng)建了 headerView ,并添加到了ListView的頭部校镐。而 setDrawableId(int id) 就是給 headerView 設(shè)置相關(guān)圖片的亿扁。

下面就是視差效果的主要實現(xiàn)代碼了:

@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) {
        if (headerView.getHeight() < headerMaxHeight) {
            int newHeight = headerView.getHeight()
                    + Math.abs(deltaY / 3);
            headerView.getLayoutParams().height = newHeight;
            headerView.requestLayout();
            float temp = 1 + (headerMaxScale - 1f) * (headerView.getHeight() - headerHeight) / (headerMaxHeight - headerHeight);
            headerView.animate().scaleX(temp)
                    .scaleY(temp).setDuration(0).start();
        }
    }
    return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}

我們重寫了 overScrollBy() 方法,當(dāng) deltaY 小于0時(即 ListView 已經(jīng)到頂端鸟廓,但是用戶手勢還是向下拉)从祝,去動態(tài)地設(shè)置 headerView 的高度以及 headerView 的 scale 值。這樣就可以產(chǎn)生 headerView 變高以及圖片放大的效果了引谜。

接下來要考慮的問題就是當(dāng)用戶松開手指時牍陌,要恢復(fù)回原來的樣子。所以我們應(yīng)該在 onTouchEvent(MotionEvent ev) 里去實現(xiàn)相關(guān)操作:

@Override
public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_UP:
                startAnim();
            break;
    }
    return super.onTouchEvent(ev);
}

// 開始執(zhí)行動畫
private void startAnim() {
    ValueAnimator animator = ValueAnimator.ofFloat(headerView.getHeight(), headerHeight);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float fraction = (float) animation.getAnimatedValue();
            headerView.getLayoutParams().height = (int) fraction;
            headerView.requestLayout();
        }
    });
    animator.setDuration(500);
    animator.setInterpolator(new LinearInterpolator());

    ValueAnimator animator2 = ValueAnimator.ofFloat(headerView.getScaleX(), 1f);
    animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float fraction = (float) animation.getAnimatedValue();
            headerView.setScaleX(fraction);
            headerView.setScaleY(fraction);
        }
    });
    animator2.setDuration(500);
    animator2.setInterpolator(new LinearInterpolator());
    animator.start();
    animator2.start();
}

上面的代碼簡單點來說员咽,就是在 ACTION_UP 時毒涧,去開始兩個屬性動畫,一個屬性動畫是將 headerView 的高度恢復(fù)成原來的值贝室,另一個屬性動畫就是把 headerView 的 scale 重新恢復(fù)為1f链嘀。相信大家都可以看懂的。

ZoomListView 整體的代碼就這些了档玻,很簡短。下面附上下載的鏈接:

ZoomListView.rar

good luck ! ~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末茫藏,一起剝皮案震驚了整個濱河市误趴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌务傲,老刑警劉巖凉当,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異售葡,居然都是意外死亡看杭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門挟伙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來楼雹,“玉大人,你說我怎么就攤上這事≈澹” “怎么了榨咐?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谴供。 經(jīng)常有香客問我块茁,道長,這世上最難降的妖魔是什么桂肌? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任数焊,我火速辦了婚禮,結(jié)果婚禮上崎场,老公的妹妹穿的比我還像新娘佩耳。我一直安慰自己,他們只是感情好照雁,可當(dāng)我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布蚕愤。 她就那樣靜靜地躺著,像睡著了一般饺蚊。 火紅的嫁衣襯著肌膚如雪萍诱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天污呼,我揣著相機(jī)與錄音裕坊,去河邊找鬼。 笑死燕酷,一個胖子當(dāng)著我的面吹牛籍凝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苗缩,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼饵蒂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了酱讶?” 一聲冷哼從身側(cè)響起退盯,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泻肯,沒想到半個月后渊迁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡灶挟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年琉朽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稚铣。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡箱叁,死狀恐怖墅垮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蝌蹂,我是刑警寧澤噩斟,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站孤个,受9級特大地震影響剃允,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜齐鲤,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一斥废、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧给郊,春花似錦牡肉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至炭庙,卻和暖如春饲窿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背焕蹄。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工逾雄, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腻脏。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓鸦泳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親永品。 傳聞我的和親對象是個殘疾皇子做鹰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容