仿QQ空間之打造個性化可拉伸頭部控制元件

codeGoogle發表於2017-09-05

最近有研究了QQ空間可拉伸頭部控制元件的listView。如何去做呢?這裡使用了自定義listView的方法。先看效果圖吧:

究竟如何去做呢?
可以用的方法有:

  • 1.繼承 extends ViewGrop
  • 2.RecycleView +Behavior+CoordinateLayout
  • 3.ViewGroup 組合控制元件
  • 4.ListView + headView

這次我們主要用原生的自定義listView去做。

  • 1.extends 繼承 ListView
  • 2.監聽過度監聽,在縮放頭部圖片重寫overScrollBy方法
  • 重寫onScrollChanged方法監聽度放大時,執行縮小圖片
  • 重寫onTouchEvent方法,執行觸控時釋放事件的處理
/**
 * 類功能描述:</br>
 *  仿QQ空間之打造個性化可拉伸頭部控制元件</br>
 * @author yuyahao
 * @version 1.0 </p> 修改時間:</br> 修改備註:</br>
 */

public class CustomerScrollZoomListView extends ListView{
    private ImageView imageView;
    private int mMeasureWeight;
    public CustomerScrollZoomListView(Context context, AttributeSet attrs) {
        super(context, attrs);
         mMeasureWeight = context.getResources().getDimensionPixelSize(R.dimen.dp200);
    }
    public CustomerScrollZoomListView(Context context) {
        super(context);
    }
    public void setZoomImageView(ImageView iView ){
         imageView = iView;

    }

    @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){//下拉過度  對圖片進行放大
            imageView.getLayoutParams().height = imageView.getHeight() - deltaY;
            imageView.requestLayout();
        }else{//上拉過度時
            imageView.getLayoutParams().height = imageView.getHeight() - deltaY;
            imageView.requestLayout();
        }
        LogUtil.i("yuyahao","deltaY:  "+deltaY);
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        //只有當imageView過度放大時,這裡才會去執行縮小
        View view = (View) imageView.getParent();
        int detalY = view.getTop();//此時 detay 為負值
       if(imageView.getHeight() > mMeasureWeight){//如果當前圖片的高度 > 初始高度
            imageView.getLayoutParams().height = imageView.getHeight() + detalY;
            imageView.requestLayout();
        }
        LogUtil.i("yuyahao","----   deltaY:  "+detalY);
        super.onScrollChanged(l, t, oldl, oldt);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_UP://鬆手釋放時
                MyCustomserAnimation myCustomserAnimation = new MyCustomserAnimation(mMeasureWeight);
                myCustomserAnimation.setDuration(300);
                myCustomserAnimation.setInterpolator(new BounceInterpolator());
                imageView.startAnimation(myCustomserAnimation);
                break;
        }
        return super.onTouchEvent(ev);
    }
    public class MyCustomserAnimation extends Animation{
        private int delay ;//高度差
        private int currentHeight ; //當前的高度
        public MyCustomserAnimation( int targetHeight){
            //this.delay = delay;
            delay = imageView.getHeight()  - targetHeight;
            currentHeight =  imageView.getHeight();
        }
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            imageView.getLayoutParams().height = (int) (currentHeight  - delay * interpolatedTime);
            imageView.requestLayout();
            super.applyTransformation(interpolatedTime, t);
        }
    }
}複製程式碼

這裡重寫了overScrollBy方法
這裡原始碼是怎麼說的呢?

  /**
     * This is called in response to an internal scroll in this view (i.e., the
     * view scrolled its own contents). This is typically as a result of
     * {@link #scrollBy(int, int)} or {@link #scrollTo(int, int)} having been
     * called.
     *
     * @param l Current horizontal scroll origin.
     * @param t Current vertical scroll origin.
     * @param oldl Previous horizontal scroll origin.
     * @param oldt Previous vertical scroll origin.
     */
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        notifySubtreeAccessibilityStateChangedIfNeeded();

        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
            postSendViewScrolledAccessibilityEventCallback();
        }

        mBackgroundSizeChanged = true;
        if (mForegroundInfo != null) {
            mForegroundInfo.mBoundsChanged = true;
        }

        final AttachInfo ai = mAttachInfo;
        if (ai != null) {
            ai.mViewScrollChanged = true;
        }

        if (mListenerInfo != null && mListenerInfo.mOnScrollChangeListener != null) {
            mListenerInfo.mOnScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);
        }
    }複製程式碼

大致意思我們都可以看得出來.大致意思是響應於該檢視中的內部滾動而呼叫的(即檢視滾動其自己的內容)比如:eltaY < 0的時候下拉過度 對圖片進行放大。 小於0時,上拉過度。

日前google上搜尋“android overscroll”,對此效果的介紹很多,但關於其具體使用方式和實現,則很少涉及,偶有提及,也經常答非所問或似是而非,反而誤導了別人。於是我查閱了android相關原始碼,並做了一些測試,在此講講我的理解。

首先是overscroll功能本身。

  • 在最頂層的View類提供了支援,可通過setOverScrollMode函式控制其出現條件。但其實View中並沒有實現overscroll功能,
  • 提供了一個輔助函式overScrollBy,該函式根據overScrollMode和內容是否需要滾動控制最大滾動範圍。
  • 最後將計算結果傳給onOverScrolled實現具體的overscroll功能
  • 但此函式在View類中是全空的
    overscroll功能真正的實現分別在ScrollView、AbsListView、HorizontalScrollView和WebView中各有一份,程式碼基本一樣。
  • 以ScrollView為例,它在處理筆點移動訊息時呼叫overScrollBy來滾動檢視,然後過載了overScrollBy函式來實現具體功能,其位置計算通過OverScroller類實現。
  • OverScroller作為一個計算引擎,應該是一個獨立的模組,具體滾動效果和範圍都不可能通過它來設定,我覺得沒有必要細看。但滾動位置最終是它給出的,那相關資料肯定要傳遞給它。
  • 回頭看overScrollBy函式,它有兩個控制overScroll出界範圍的引數,幾個實現裡面都是取自ViewConfiguration.getScaledOverscrollDistance,而這個引數的值在我的原始碼中都是0,而且我沒找到任何可以影響其結果的設定。

Activity中的程式碼:

/**
 * 類功能描述:</br>
 *  仿QQ空間之打造個性化可拉伸頭部控制元件</br>
 * @author yuyahao
 * @version 1.0 </p> 修改時間:</br> 修改備註:</br>
 */
public class CustomerScrollZoomListViewActivity extends BaseActivity{
    @Bind(R.id.lv_customser_zoomscroll)
    CustomerScrollZoomListView lv_customser_zoomscroll;
    private List<Student> list = new ArrayList<>();
    private ZoomScrollListViewAdapter zoomScrollListViewAdapter;
    private View headView;
    @Override
    protected void initData() {
        setContentView(R.layout.activity_customerscrollzoomlistview);
        ButterKnife.bind(this);
        list.addAll(ServiceData.getStudentList());

        zoomScrollListViewAdapter = new ZoomScrollListViewAdapter(CustomerScrollZoomListViewActivity.this,list,R.layout.item_xzoomscrollview);
        headView = LayoutInflater.from(CustomerScrollZoomListViewActivity.this).inflate(R.layout.zoom_headview,null);
        ImageView iamgeView = (ImageView) headView.findViewById(R.id.iv_zoom_head);
        lv_customser_zoomscroll.setZoomImageView(iamgeView);
        lv_customser_zoomscroll.addHeaderView(headView);
        lv_customser_zoomscroll.setAdapter(zoomScrollListViewAdapter);

    }

}複製程式碼

xml佈局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.yolo.myapplication.activity.view.CustomerScrollZoomListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/lv_customser_zoomscroll"
        />
</LinearLayout>複製程式碼

adapter介面卡

public class ZoomScrollListViewAdapter extends CommonAdapter<Student> {
    private Context context;

    public ZoomScrollListViewAdapter(Context context, List<Student> listDatas, int layoutId) {
        super(context, listDatas, layoutId);
        this.context = context;
    }

    @Override
    protected void fillData(ViewHolder holder, int position) {
        TextView actNum = holder.getView(R.id.team_item_active_num);
        TextView time = holder.getView(R.id.team_item_time);
        TextView title = holder.getView(R.id.team_item_title);
        ImageView icon = holder.getView(R.id.team_item_icon);
        Student item = listDatas.get(position);
        actNum.setText(String.valueOf(item.getList().size()) + "");
        time.setText(String.valueOf(item.getList().get(0).getPerListenceName()) + "");
        title.setText(item.getName());
    }
}複製程式碼

adapter介面卡請參考:
教你打造一個萬能的介面卡
item:佈局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:gravity="center_vertical"
    android:layout_height="match_parent">
    <ImageView
        android:layout_width="@dimen/dp80"
        android:layout_height="@dimen/dp80"
        android:background="@drawable/img2"
        android:scaleType="fitXY"
        android:id="@+id/team_item_icon"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_marginLeft="@dimen/dp10"
        android:orientation="vertical"
        android:layout_height="@dimen/dp80">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/team_item_title"
            android:gravity="center_vertical"
            android:layout_weight="1"
            android:text="卡卡羅特"/>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_weight="1"
            android:layout_height="match_parent"
            android:gravity="center_vertical">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="天不排名"
                android:id="@+id/team_item_time"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/dp20"
                android:text="戰鬥值: "

                android:id="@+id/team_item_active_num"/>

        </LinearLayout>
    </LinearLayout>
</LinearLayout>複製程式碼

專案原始碼地址:MyZoomListView.rar

如果你覺得此文對您有所幫助,歡迎入群 QQ交流群 :232203809
微信公眾號:終端研發部

專攻技術
專攻技術

(歡迎關注學習和交流)

相關文章