快速實現 ListView下拉,圖片放大重新整理操作,listview下拉

來源:互聯網
上載者:User

快速實現 ListView下拉,圖片放大重新整理操作,listview下拉

今天要寫的這個效果屬於重新整理類,比較實用,像很多流行的 app 都是用了這種效果,大家熟知的QQ空間、微博個人首頁等,這個效果在 github 上也有別人實現好的源碼,點擊查看。這裡也參考了上面的源碼;還是那句話,看 blog主要是學習其中的原理和思路。

動態

圖片放大的原理是什麼呢?
通過改變圖片顯示控制項 ImageView 的父控制項的高度,比如這裡的頭部 View 是一個 FrameLayout,FrameLayout 中再 通過 add 方法把圖片 View 添加進去,addView(ImageView),ImageView有幾個屬性是要特別注意的,ImageView 的放縮類型為從中間截取
setScaleType(ImageView.ScaleType.CENTER_CROP);
並且寬高設為匹配父控制項;所以想要圖片有放大效果,只需設定 FrameLayout 的 LayoutParams 中的 height值,通過改變 height 的值從而改變 ImageView 的顯示高度。講的有點混亂,可以結合下面的代碼來理解。

如果你是對手勢事件處理很瞭解的朋友,對這個效果的實現應該沒有什麼難度,唯一的一點就是判斷何時該放大圖片,何時該滾動ListView。
這裡就直接貼代碼:

/**
* Created by gyzhong on 15/3/22.
*/
public class PullZoomListView extends ListView {

/*頭部View 的容器*/private FrameLayout mHeaderContainer;/*頭部View 的圖片*/private ImageView mHeaderImg;/*螢幕的高度*/private int mScreenHeight;/*螢幕的寬度*/private int mScreenWidth;private int mHeaderHeight;/*無效的點*/private static final int INVALID_POINTER = -1;/*滑動動畫執行的時間*/private static final int MIN_SETTLE_DURATION = 200; // ms/*定義了一個時間插值器,根據ViewPage控制項來定義的*/private static final Interpolator sInterpolator = new Interpolator() {    public float getInterpolation(float t) {        t -= 1.0f;        return t * t * t * t * t + 1.0f;    }};/*記錄上一次手指觸摸的點*/private float mLastMotionX;private float mLastMotionY;/*當前活動的點Id,有效點的Id*/protected int mActivePointerId = INVALID_POINTER;/*開始滑動的標誌距離*/private int mTouchSlop;/*放大的倍數*/private float mScale;/*上一次放大的倍數*/private float mLastScale;/*最大放大的倍數*/private final float mMaxScale = 2.0f;/*是否需要禁止ListView 的事件響應*/private boolean isNeedCancelParent;/*這個不解釋*/private OnScrollListener mScrollListener ;/*下拉重新整理的閾值*/private final float REFRESH_SCALE = 1.20F;/*下拉重新整理監聽*/private OnRefreshListener mRefreshListener ;public PullZoomListView(Context context) {    super(context);    init(context);}public PullZoomListView(Context context, AttributeSet attrs) {    super(context, attrs);    init(context);}public PullZoomListView(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);    init(context);}private void init(Context context) {    /*這裡擷取的是一個無用值,可忽略*/    final ViewConfiguration configuration = ViewConfiguration.get(context);    mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);    /*建立頭部View 的容器*/    mHeaderContainer = new FrameLayout(context);    /*擷取螢幕的像素值*/    DisplayMetrics metrics = new DisplayMetrics();    ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metrics);    mScreenHeight = metrics.heightPixels;    mScreenWidth = metrics.widthPixels;    /*設定頭部View 的初始大小*/    mHeaderHeight = (int) ((9 * 1.0f / 16) * mScreenWidth);    LayoutParams absLayoutParams = new LayoutParams(mScreenWidth, mHeaderHeight);    mHeaderContainer.setLayoutParams(absLayoutParams);    /*建立圖片顯示的View*/    mHeaderImg = new ImageView(context);    FrameLayout.LayoutParams imgLayoutParams = new FrameLayout.LayoutParams            (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);    mHeaderImg.setScaleType(ImageView.ScaleType.CENTER_CROP);    mHeaderImg.setLayoutParams(imgLayoutParams);    mHeaderContainer.addView(mHeaderImg);    /*增加頭部View*/    addHeaderView(mHeaderContainer);    /*設定監聽事件*/    super.setOnScrollListener(new InternalScrollerListener() );}/*處理事件用*/@Overridepublic boolean onTouchEvent(MotionEvent ev) {    final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;    switch (action) {        case MotionEvent.ACTION_DOWN:            /*計算 x,y 的距離*/            int index = MotionEventCompat.getActionIndex(ev);            mActivePointerId = MotionEventCompat.getPointerId(ev, index);            if (mActivePointerId == INVALID_POINTER)                break;            mLastMotionX = MotionEventCompat.getX(ev, index);            mLastMotionY = MotionEventCompat.getY(ev, index);            // 結束動畫,目前沒做處理,可忽略            abortAnimation();            /*計算算一次放縮的比例*/            mLastScale = (this.mHeaderContainer.getBottom() / this.mHeaderHeight);            /*當按下的時候把這個標誌為設為有效*/            isNeedCancelParent = true ;            break;        case MotionEvent.ACTION_MOVE:            int indexMove = MotionEventCompat.getActionIndex(ev);            mActivePointerId = MotionEventCompat.getPointerId(ev, indexMove);            if (mActivePointerId == INVALID_POINTER) {                /*這裡相當於鬆手*/                finishPull();                isNeedCancelParent = true ;            } else {                /*這是一個關索引值,通過這個值來判斷是否需要放大圖片*/                if (mHeaderContainer.getBottom() >= mHeaderHeight) {                    ViewGroup.LayoutParams params = this.mHeaderContainer                            .getLayoutParams();                    final float y = MotionEventCompat.getY(ev, indexMove);                    float dy = y - mLastMotionY;                    float f = ((y - this.mLastMotionY + this.mHeaderContainer                            .getBottom()) / this.mHeaderHeight - this.mLastScale)                            / 2.0F + this.mLastScale;                    if ((this.mLastScale <= 1.0D) && (f <= this.mLastScale)) {                        params.height = this.mHeaderHeight;                        this.mHeaderContainer                                .setLayoutParams(params);                        return super.onTouchEvent(ev);                    }                    /*這裡設定緊湊度*/                    dy = dy * 0.5f * (mHeaderHeight * 1.0f / params.height);                    mLastScale = (dy + params.height) * 1.0f / mHeaderHeight;                    mScale = clamp(mLastScale, 1.0f, mMaxScale);

// Log.v(“zgy”, “=======mScale=====” + mLastScale+”,f = “+f);

                    params.height = (int) (mHeaderHeight * mScale);                    mHeaderContainer.setLayoutParams(params);                    mLastMotionY = y;                    /*這裡,如果圖片有放大,則屏蔽ListView 的其他事件響應*/                    if(isNeedCancelParent ){                        isNeedCancelParent = false;                        MotionEvent motionEvent = MotionEvent.obtain(ev);                        motionEvent.setAction(MotionEvent.ACTION_CANCEL);                        super.onTouchEvent(motionEvent);                    }                    return true;                }                mLastMotionY = MotionEventCompat.getY(ev, indexMove);            }            break;        case MotionEvent.ACTION_UP:            /*結束事件響應,做相應的操作*/            finishPull();            break;        case MotionEvent.ACTION_POINTER_UP:            /*這裡需要注意,多點處理,這裡的處理方式是:如果有兩個手指按下,抬起的是後按下的手指,則不做處理            * 如果抬起的是最先按下的手指,則複原圖片效果。            * */            int pointUpIndex = MotionEventCompat.getActionIndex(ev);            int pointId = MotionEventCompat.getPointerId(ev, pointUpIndex);            if (pointId == mActivePointerId) {                /*鬆手執行結束拖拽操作*/                /*結束事件響應,做相應的操作*/                finishPull();            }            break;    }    return super.onTouchEvent(ev);}@Overridepublic void setOnScrollListener(OnScrollListener l) {    mScrollListener = l ;}private void abortAnimation() {    /*啥都沒做,暫時沒做而已*/}private void finishPull() {    mActivePointerId = INVALID_POINTER;    /*這是一個閾值,如果成立,則表示圖片已經放大了,在手指抬起的時候需要複原圖片*/    if (mHeaderContainer.getBottom() > mHeaderHeight){

// Log.v(“zgy”, “===super====onTouchEvent========”);
/這裡是下拉重新整理的閾值,當達到了,則表示需要重新整理,可以添加重新整理動畫/
if (mScale > REFRESH_SCALE){
if (mRefreshListener != null){
mRefreshListener.onRefresh();
}
}
/圖片複原動畫/
pullBackAnimation();
}
}

/** * 這是屬性動畫的知識,不懂的可以去看看屬性動畫的知識 */private void pullBackAnimation(){    ValueAnimator pullBack = ValueAnimator.ofFloat(mScale , 1.0f);    pullBack.setInterpolator(sInterpolator);    pullBack.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {        @Override        public void onAnimationUpdate(ValueAnimator animation) {            float value = (float) animation.getAnimatedValue();            LayoutParams params = (LayoutParams) mHeaderContainer.getLayoutParams();            params.height = (int) (mHeaderHeight * value);            mHeaderContainer.setLayoutParams(params);        }    });    pullBack.setDuration((long) (MIN_SETTLE_DURATION*mScale));    pullBack.start();}/** * 通過事件和點的 id 來擷取點的索引 * * @param ev * @param id * @return */private int getPointerIndex(MotionEvent ev, int id) {    int activePointerIndex = MotionEventCompat.findPointerIndex(ev, id);    if (activePointerIndex == -1)        mActivePointerId = INVALID_POINTER;    return activePointerIndex;}public void setOnRefreshListener(OnRefreshListener l){    mRefreshListener = l ;}public ImageView getHeaderImageView() {    return this.mHeaderImg;}private float clamp(float value, float min, float max) {    return Math.min(Math.max(value, min), max);}private class InternalScrollerListener implements OnScrollListener{    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {        if (mScrollListener != null){            mScrollListener.onScrollStateChanged(view,scrollState);        }    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {        float diff = mHeaderHeight - mHeaderContainer.getBottom();        if ((diff > 0.0F) && (diff < mHeaderHeight)) {            int i = (int) (0.3D * diff);            mHeaderImg.scrollTo(0, -i);        } else if (mHeaderImg.getScrollY() != 0) {            mHeaderImg.scrollTo(0, 0);        }        Log.v("zgy","=========height==="+getScrolledY());        if (mScrollListener != null){            mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);        }    }}public int getScrolledY() {    View c = getChildAt(0);    if (c == null) {        return 0;    }    int firstVisiblePosition = getFirstVisiblePosition();    int top = c.getTop();    int headerHeight = 0;    if (firstVisiblePosition >= 1) {        headerHeight = mHeaderHeight;    }    return -top + firstVisiblePosition * c.getHeight() + headerHeight;}public interface OnRefreshListener {   void onRefresh() ;}public void computeRefresh(){    if (mActivePointerId != INVALID_POINTER){    }}

}

比較難理解的地方都做了注釋,所以。。。應該還是很好理解的。

總結:
今天 blog的一個痛點就是,手勢的處理,下拉放大的條件判斷;針對這種問題,我也沒有很好的解決方案,我的經驗就是,直接通過 列印 Log 的方式來尋找規律,因為有的時候想的挺煩的,而且邏輯很容易混亂。
接著是圖片放大的原理,知道了就很好實現此功能。

源碼

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.