美文网首页andnroid待集成CoordinatorLayout
CoordinatorLayout自定义下拉刷新

CoordinatorLayout自定义下拉刷新

作者: 那个唐僧 | 来源:发表于2017-01-11 14:56 被阅读2854次

    看了一个理财类app的下拉刷新.感觉有点屌.所以特此模仿一下.
    先看正版演示:

    aa.gif

    再看我这盗版演示:


    aa1.gif

    不得不说的是我的有点丑啊.

    最上面的下拉头的问题还没有解决的很完美.有白色的边出现.

    说下怎么实现的吧,
    1,肯定是先创建个含有CoordinatorLayout布局的scrollingActivity了
    2,然后我开始的是头像的滑动的放大和缩小.这个用了一个哥们的自定义的toolbar,代码如下:

    package com.sloydev.collapsingavatartoolbar;
    
    import android.content.Context;
    import android.content.res.Resources;
    import android.content.res.TypedArray;
    import android.support.annotation.NonNull;
    import android.support.design.widget.AppBarLayout;
    import android.support.v7.widget.Toolbar;
    import android.util.AttributeSet;
    import android.util.TypedValue;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.ViewParent;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    
    public class CollapsingAvatarToolbar extends LinearLayout implements AppBarLayout.OnOffsetChangedListener {
    
        private View avatarView;
        private TextView titleView;
    
        private float collapsedPadding;
        private float expandedPadding;
    
        private float expandedImageSize;
        private float collapsedImageSize;
    
        private float collapsedTextSize;
        private float expandedTextSize;
    
        private boolean valuesCalculatedAlready = false;
        private Toolbar toolbar;
        private AppBarLayout appBarLayout;
        private float collapsedHeight;
        private float expandedHeight;
        private float maxOffset;
    
        private CollapseChangedListener collapseChangedListener;
    
        public CollapsingAvatarToolbar(Context context) {
            this(context, null);
            init();
        }
    
        public CollapsingAvatarToolbar(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
    
            TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CollapsingAvatarToolbar, 0, 0);
    
            try {
                collapsedPadding = a.getDimension(R.styleable.CollapsingAvatarToolbar_collapsedPadding, -1);
                expandedPadding = a.getDimension(R.styleable.CollapsingAvatarToolbar_expandedPadding, -1);
    
                collapsedImageSize = a.getDimension(R.styleable.CollapsingAvatarToolbar_collapsedImageSize, -1);
                expandedImageSize = a.getDimension(R.styleable.CollapsingAvatarToolbar_expandedImageSize, -1);
    
                collapsedTextSize = a.getDimension(R.styleable.CollapsingAvatarToolbar_collapsedTextSize, -1);
                expandedTextSize = a.getDimension(R.styleable.CollapsingAvatarToolbar_expandedTextSize, -1);
            } finally {
                a.recycle();
            }
    
            final Resources resources = getResources();
            if (collapsedPadding < 0) {
                collapsedPadding = resources.getDimension(R.dimen.default_collapsed_padding);
            }
            if (expandedPadding < 0) {
                expandedPadding = resources.getDimension(R.dimen.default_expanded_padding);
            }
            if (collapsedImageSize < 0) {
                collapsedImageSize = resources.getDimension(R.dimen.default_collapsed_image_size);
            }
            if (expandedImageSize < 0) {
                expandedImageSize = resources.getDimension(R.dimen.default_expanded_image_size);
            }
            if (collapsedTextSize < 0) {
                collapsedTextSize = resources.getDimension(R.dimen.default_collapsed_text_size);
            }
            if (expandedTextSize < 0) {
                expandedTextSize = resources.getDimension(R.dimen.default_expanded_text_size);
            }
        }
    
        public void setCollapseChangedListener(CollapseChangedListener collapseChangedListener) {
            this.collapseChangedListener = collapseChangedListener;
        }
    
        private void init() {
            setOrientation(HORIZONTAL);
        }
    
        @NonNull
        private AppBarLayout findParentAppBarLayout() {
            ViewParent parent = this.getParent();
            if (parent instanceof AppBarLayout) {
                return ((AppBarLayout) parent);
            } else if (parent.getParent() instanceof AppBarLayout) {
                return ((AppBarLayout) parent.getParent());
            } else {
                throw new IllegalStateException("Must be inside an AppBarLayout"); //TODO actually, a collapsingtoolbar
            }
        }
    
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            findViews();
            if (!isInEditMode()) {
                appBarLayout.addOnOffsetChangedListener(this);
            } else {
                setExpandedValuesForEditMode();
            }
        }
    
        private void setExpandedValuesForEditMode() {
            calculateValues();
            updateViews(1f, 0);
        }
    
        private void findViews() {
            appBarLayout = findParentAppBarLayout();
            toolbar = findSiblingToolbar();
            avatarView = findAvatar();
            titleView = findTitle();
        }
    
        @NonNull
        private View findAvatar() {
            View avatar = this.findViewById(R.id.cat_avatar);
            if (avatar == null) {
                throw new IllegalStateException("View with id ta_avatar not found");
            }
            return avatar;
        }
    
        @NonNull
        private TextView findTitle() {
            TextView title = (TextView) this.findViewById(R.id.cat_title);
            if (title == null) {
                throw new IllegalStateException("TextView with id ta_title not found");
            }
            return title;
        }
    
        @NonNull
        private Toolbar findSiblingToolbar() {
            ViewGroup parent = ((ViewGroup) this.getParent());
            for (int i = 0, c = parent.getChildCount(); i < c; i++) {
                View child = parent.getChildAt(i);
                if (child instanceof Toolbar) {
                    return (Toolbar) child;
                }
            }
            throw new IllegalStateException("No toolbar found as sibling");
        }
    
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int offset) {
            if (!valuesCalculatedAlready) {
                calculateValues();
                valuesCalculatedAlready = true;
            }
            float collapsedProgress = -offset / maxOffset;
            updateViews(collapsedProgress, offset);
            notifyListener(collapsedProgress);
        }
    
        private void calculateValues() {
            collapsedHeight = toolbar.getHeight();
            expandedHeight = appBarLayout.getHeight() - toolbar.getHeight();
            maxOffset = expandedHeight;
        }
    
        private void updateViews(float collapsedProgress, int currentOffset) {
            float expandedProgress = 1 - collapsedProgress;
            float translation = -currentOffset + ((float) toolbar.getHeight() * expandedProgress);
    
            float currHeight = collapsedHeight + (expandedHeight - collapsedHeight) * expandedProgress;
            float currentPadding = expandedPadding + (collapsedPadding - expandedPadding) * collapsedProgress;
            float currentImageSize = collapsedImageSize + (expandedImageSize - collapsedImageSize) * expandedProgress;
            float currentTextSize = collapsedTextSize + (expandedTextSize - collapsedTextSize) * expandedProgress;
    
            setContainerOffset(translation);
            setContainerHeight((int) currHeight);
            setPadding((int) currentPadding);
            setAvatarSize((int) currentImageSize);
            setTextSize(currentTextSize);
        }
    
        private void setContainerOffset(float translation) {
            this.setTranslationY(translation);
        }
    
        private void setContainerHeight(int currHeight) {
            this.getLayoutParams().height = currHeight;
        }
    
        private void setPadding(int currentPadding) {
            this.setPadding(currentPadding, 0, 0, 0);
        }
    
        private void setTextSize(float currentTextSize) {
            titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, currentTextSize);
        }
    
        private void setAvatarSize(int currentImageSize) {
            avatarView.getLayoutParams().height = currentImageSize;
            avatarView.getLayoutParams().width = currentImageSize;
        }
    
        private void notifyListener(float collapsedProgress) {
            if (collapseChangedListener != null) {
                collapseChangedListener.onCollapseChanged(collapsedProgress);
            }
        }
    
        public interface CollapseChangedListener {
    
            void onCollapseChanged(float collapsedProgress);
        }
    }
    
    

    3,头像问题解决了之后是在滑动的时候toolbar的颜色的渐变并且还有状态栏的颜色是个toolbar的颜色的一致的渐变,这里的代码在后面会贴出
    4,头像的问题解决了之后就该到了让人头疼的下拉刷新了.这个刷新是嵌套的CoordinatorLayout,在做这个效果的时候度娘都要快被我查烂了.试过各种办法.都是不了了之.当时觉得哎,好烦.后来静下心之后,有篇文章也是写下拉刷新的,用的是swipeRefreshLayout的改进,我采取了这个方法,然后自己在巨人的肩膀上在修改.代码如下:

    package com.ccstest.views;
    
    import android.content.Context;
    import android.graphics.Color;
    import android.support.v4.view.MotionEventCompat;
    import android.support.v4.view.ViewCompat;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewConfiguration;
    import android.view.ViewGroup;
    import android.widget.AbsListView;
    import android.widget.ImageView;
    import android.widget.Scroller;
    
    import com.ccstest.R;
    
    /**
     * Created by AItsuki on 2016/6/13.
     * -
     */
    public class RefreshLayout extends ViewGroup {
    
        private static final String TAG = "RefreshLayout";
        private static final float DRAG_RATE = 0.5f;
        private static final int INVALID_POINTER = -1;
    
        // scroller duration
        private static final int SCROLL_TO_TOP_DURATION = 800;
        private static final int SCROLL_TO_REFRESH_DURATION = 250;
        private static final long SHOW_COMPLETED_TIME = 500;
    
        private View refreshHeader;
        private View target;
        private int currentTargetOffsetTop; // target/header偏移距离
        private int lastTargetOffsetTop;
    
        private boolean hasMeasureHeader;   // 是否已经计算头部高度
        private int touchSlop;
        private int headerHeight;       // header高度
        private int totalDragDistance;  // 需要下拉这个距离才进入松手刷新状态,默认和header高度一致
        private int maxDragDistance;
        private int activePointerId;
        private boolean isTouch;
        private boolean hasSendCancelEvent;
        private float lastMotionX;
        private float lastMotionY;
        private float initDownY;
        private float initDownX;
        private static final int START_POSITION = 0;
        private MotionEvent lastEvent;
        private boolean mIsBeginDragged;
        private AutoScroll autoScroll;
        private State state = State.RESET;
        private OnRefreshListener refreshListener;
        private boolean isAutoRefresh;
    
    
        // 刷新成功,显示500ms成功状态再滚动回顶部
        private Runnable delayToScrollTopRunnable = new Runnable() {
            @Override
            public void run() {
                autoScroll.scrollTo(START_POSITION, SCROLL_TO_TOP_DURATION);
            }
        };
    
        private Runnable autoRefreshRunnable = new Runnable() {
            @Override
            public void run() {
                // 标记当前是自动刷新状态,finishScroll调用时需要判断
                // 在actionDown事件中重新标记为false
                isAutoRefresh = true;
                changeState(State.PULL);
                autoScroll.scrollTo(totalDragDistance, SCROLL_TO_REFRESH_DURATION);
            }
        };
    
    
        public RefreshLayout(Context context) {
            this(context, null);
        }
    
        public RefreshLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
            autoScroll = new AutoScroll();
    
            // 添加默认的头部,先简单的用一个ImageView代替头部
            ImageView imageView = new ImageView(context);
            imageView.setImageResource(R.drawable.gold);
            imageView.setBackgroundColor(Color.BLACK);
            setRefreshHeader(imageView);
        }
    
        /**
         * 设置自定义header
         */
        public void setRefreshHeader(View view) {
            if (view != null && view != refreshHeader) {
                removeView(refreshHeader);
    
                // 为header添加默认的layoutParams
                LayoutParams layoutParams = view.getLayoutParams();
                if (layoutParams == null) {
                    layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, Utils.dp2px(getContext(), 80));
    //                layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
                    view.setLayoutParams(layoutParams);
                }
                refreshHeader = view;
                addView(refreshHeader);
            }
        }
    
        public void setRefreshListener(OnRefreshListener refreshListener) {
            this.refreshListener = refreshListener;
        }
    
        public void refreshComplete() {
            changeState(State.COMPLETE);
            // if refresh completed and the target at top, change state to reset.
            if (currentTargetOffsetTop == START_POSITION) {
                changeState(State.RESET);
            } else {
                // waiting for a time to show refreshView completed state.
                // at next touch event, remove this runnable
                if (!isTouch) {
                    postDelayed(delayToScrollTopRunnable, SHOW_COMPLETED_TIME);
                }
            }
        }
    
        public void autoRefresh() {
            autoRefresh(500);
        }
    
        /**
         * 在onCreate中调用autoRefresh,此时View可能还没有初始化好,需要延长一段时间执行。
         *
         * @param duration 延时执行的毫秒值
         */
        public void autoRefresh(long duration) {
            if (state != State.RESET) {
                return;
            }
            postDelayed(autoRefreshRunnable, duration);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            if (target == null) {
                ensureTarget();
            }
    
            if (target == null) {
                return;
            }
    
            // ----- measure target -----
            // target占满整屏
            target.measure(MeasureSpec.makeMeasureSpec(
                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                    MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY));
    
            // ----- measure refreshView-----
            measureChild(refreshHeader, widthMeasureSpec, heightMeasureSpec);
            if (!hasMeasureHeader) { // 防止header重复测量
                hasMeasureHeader = true;
                headerHeight = refreshHeader.getMeasuredHeight(); // header高度
                totalDragDistance = headerHeight;   // 需要pull这个距离才进入松手刷新状态
                if (maxDragDistance == 0) {  // 默认最大下拉距离为控件高度的五分之四
                    maxDragDistance = totalDragDistance*4/5;
                }
            }
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            final int width = getMeasuredWidth();
            final int height = getMeasuredHeight();
            if (getChildCount() == 0) {
                return;
            }
    
            if (target == null) {
                ensureTarget();
            }
            if (target == null) {
                return;
            }
    
            // target铺满屏幕
            final View child = target;
            final int childLeft = getPaddingLeft();
            final int childTop = getPaddingTop() + currentTargetOffsetTop;
            final int childWidth = width - getPaddingLeft() - getPaddingRight();
            final int childHeight = height - getPaddingTop() - getPaddingBottom();
            child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
    
            // header放到target的上方,水平居中
            int refreshViewWidth = refreshHeader.getMeasuredWidth();
            refreshHeader.layout((width / 2 - refreshViewWidth / 2),
                    -headerHeight + currentTargetOffsetTop,
                    (width / 2 + refreshViewWidth / 2),
                    currentTargetOffsetTop);
        }
    
        /**
         * 将第一个Child作为target
         */
        private void ensureTarget() {
            // Don't bother getting the parent height if the parent hasn't been laid
            // out yet.
            if (target == null) {
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    if (!child.equals(refreshHeader)) {
                        target = child;
                        break;
                    }
                }
            }
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (!isEnabled() || target == null) {
                return super.dispatchTouchEvent(ev);
            }
    
            final int actionMasked = ev.getActionMasked(); // support Multi-touch
            switch (actionMasked) {
                case MotionEvent.ACTION_DOWN:
                    activePointerId = ev.getPointerId(0);
                    isAutoRefresh = false;
                    isTouch = true;
                    hasSendCancelEvent = false;
                    mIsBeginDragged = false;
                    lastTargetOffsetTop = currentTargetOffsetTop;
                    currentTargetOffsetTop = target.getTop();
                    initDownX = lastMotionX = ev.getX(0);
                    initDownY = lastMotionY = ev.getY(0);
                    autoScroll.stop();
                    removeCallbacks(delayToScrollTopRunnable);
                    removeCallbacks(autoRefreshRunnable);
                    super.dispatchTouchEvent(ev);
                    return true;    // return true,否则可能接受不到move和up事件
    
                case MotionEvent.ACTION_MOVE:
                    if (activePointerId == INVALID_POINTER) {
                        Log.e(TAG, "Got ACTION_MOVE event but don't have an active pointer id.");
                        return super.dispatchTouchEvent(ev);
                    }
                    lastEvent = ev;
                    float x = ev.getX(MotionEventCompat.findPointerIndex(ev, activePointerId));
                    float y = ev.getY(MotionEventCompat.findPointerIndex(ev, activePointerId));
                    float yDiff = y - lastMotionY;
                    float offsetY = yDiff * DRAG_RATE;
                    lastMotionX = x;
                    lastMotionY = y;
    
                    if (!mIsBeginDragged && Math.abs(y - initDownY) > touchSlop) {
                        mIsBeginDragged = true;
                    }
    
                    if (mIsBeginDragged) {
                        boolean moveDown = offsetY > 0; // ↓
                        boolean canMoveDown = canChildScrollUp();
                        boolean moveUp = !moveDown;     // ↑
                        boolean canMoveUp = currentTargetOffsetTop > START_POSITION;
    
                        // 判断是否拦截事件
                        if ((moveDown && !canMoveDown) || (moveUp && canMoveUp)) {
                            moveSpinner(offsetY);
                            return true;
                        }
                    }
                    break;
    
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    isTouch = false;
                    if (currentTargetOffsetTop > START_POSITION) {
                        finishSpinner();
                    }
                    activePointerId = INVALID_POINTER;
                    break;
    
                case MotionEvent.ACTION_POINTER_DOWN:
                    int pointerIndex = MotionEventCompat.getActionIndex(ev);
                    if (pointerIndex < 0) {
                        Log.e(TAG, "Got ACTION_POINTER_DOWN event but have an invalid action index.");
                        return super.dispatchTouchEvent(ev);
                    }
                    lastMotionX = ev.getX(pointerIndex);
                    lastMotionY = ev.getY(pointerIndex);
                    lastEvent = ev;
                    activePointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
                    break;
    
                case MotionEvent.ACTION_POINTER_UP:
                    onSecondaryPointerUp(ev);
                    lastMotionY = ev.getY(ev.findPointerIndex(activePointerId));
                    lastMotionX = ev.getX(ev.findPointerIndex(activePointerId));
                    break;
            }
            return super.dispatchTouchEvent(ev);
        }
    
    
        private void moveSpinner(float diff) {
            int offset = Math.round(diff);
            if (offset == 0) {
                return;
            }
    
            // 发送cancel事件给child
            if (!hasSendCancelEvent && isTouch && currentTargetOffsetTop > START_POSITION) {
                sendCancelEvent();
                hasSendCancelEvent = true;
            }
    
            int targetY = Math.max(0, currentTargetOffsetTop + offset); // target不能移动到小于0的位置……
            // y = x - (x/2)^2
            float extraOS = targetY - totalDragDistance;
            float slingshotDist = totalDragDistance;
            float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2) / slingshotDist);
            float tensionPercent = (float) (tensionSlingshotPercent - Math.pow(tensionSlingshotPercent / 2, 2));
    
            if (offset > 0) { // 下拉的时候才添加阻力
                offset = (int) (offset * (1f - tensionPercent));
                targetY = Math.max(0, currentTargetOffsetTop + offset);
            }
    
            // 1. 在RESET状态时,第一次下拉出现header的时候,设置状态变成PULL
            if (state == State.RESET && currentTargetOffsetTop == START_POSITION && targetY > 0) {
                changeState(State.PULL);
            }
    
            // 2. 在PULL或者COMPLETE状态时,header回到顶部的时候,状态变回RESET
            if (currentTargetOffsetTop > START_POSITION && targetY <= START_POSITION) {
                if (state == State.PULL || state == State.COMPLETE) {
                    changeState(State.RESET);
                }
            }
    
            // 3. 如果是从底部回到顶部的过程(往上滚动),并且手指是松开状态, 并且当前是PULL状态,状态变成LOADING,这时候我们需要强制停止autoScroll
            if (state == State.PULL && !isTouch && currentTargetOffsetTop > totalDragDistance && targetY <= totalDragDistance) {
                autoScroll.stop();
                changeState(State.LOADING);
                if (refreshListener != null) {
                    refreshListener.onRefresh();
                }
                // 因为判断条件targetY <= totalDragDistance,会导致不能回到正确的刷新高度(有那么一丁点偏差),调整change
                int adjustOffset = totalDragDistance - targetY;
                offset += adjustOffset;
            }
    
            setTargetOffsetTopAndBottom(offset);
    
            // 别忘了回调header的位置改变方法。
            if (refreshHeader instanceof RefreshHeader) {
                ((RefreshHeader) refreshHeader)
                        .onPositionChange(currentTargetOffsetTop, lastTargetOffsetTop, totalDragDistance, isTouch, state);
    
            }
    
        }
    
        private void finishSpinner() {
            if (state == State.LOADING) {
                if (currentTargetOffsetTop > totalDragDistance) {
                    autoScroll.scrollTo(totalDragDistance, SCROLL_TO_REFRESH_DURATION);
                }
            } else {
                autoScroll.scrollTo(START_POSITION, SCROLL_TO_TOP_DURATION);
            }
        }
    
    
        private void changeState(State state) {
            this.state = state;
    
    //        Toast.makeText(getContext(), state.toString(), Toast.LENGTH_SHORT).show();
            RefreshHeader refreshHeader = this.refreshHeader instanceof RefreshHeader ? ((RefreshHeader) this.refreshHeader) : null;
            if (refreshHeader != null) {
                switch (state) {
                    case RESET:
                        refreshHeader.reset();
                        break;
                    case PULL:
                        refreshHeader.pull();
                        break;
                    case LOADING:
                        refreshHeader.refreshing();
                        break;
                    case COMPLETE:
                        refreshHeader.complete();
                        break;
                }
            }
        }
    
        //add
    
    
    
    
        private void setTargetOffsetTopAndBottom(int offset) {
            if (offset == 0) {
                return;
            }
            target.offsetTopAndBottom(offset);
            refreshHeader.offsetTopAndBottom(offset);
            lastTargetOffsetTop = currentTargetOffsetTop;
            currentTargetOffsetTop = target.getTop();
    //        Log.e(TAG, "moveSpinner: currentTargetOffsetTop = "+ currentTargetOffsetTop);
            invalidate();
        }
    
        private void sendCancelEvent() {
            if (lastEvent == null) {
                return;
            }
            MotionEvent ev = MotionEvent.obtain(lastEvent);
            ev.setAction(MotionEvent.ACTION_CANCEL);
            super.dispatchTouchEvent(ev);
        }
    
        private void onSecondaryPointerUp(MotionEvent ev) {
            final int pointerIndex = MotionEventCompat.getActionIndex(ev);
            final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
            if (pointerId == activePointerId) {
                // This was our active pointer going up. Choose a new
                // active pointer and adjust accordingly.
                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                lastMotionY = ev.getY(newPointerIndex);
                lastMotionX = ev.getX(newPointerIndex);
                activePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
            }
        }
    
        public boolean canChildScrollUp() {
            if (android.os.Build.VERSION.SDK_INT < 14) {
                if (target instanceof AbsListView) {
                    final AbsListView absListView = (AbsListView) target;
                    return absListView.getChildCount() > 0
                            && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
                            .getTop() < absListView.getPaddingTop());
                } else {
                    return ViewCompat.canScrollVertically(target, -1) || target.getScrollY() > 0;
                }
            } else {
                return ViewCompat.canScrollVertically(target, -1);
            }
        }
    
    
        private class AutoScroll implements Runnable {
            private Scroller scroller;
            private int lastY;
    
            public AutoScroll() {
                scroller = new Scroller(getContext());
            }
    
            @Override
            public void run() {
                boolean finished = !scroller.computeScrollOffset() || scroller.isFinished();
                if (!finished) {
                    int currY = scroller.getCurrY();
                    int offset = currY - lastY;
                    lastY = currY;
                    moveSpinner(offset);
                    post(this);
                    onScrollFinish(false);
                } else {
                    stop();
                    onScrollFinish(true);
                }
            }
    
            public void scrollTo(int to, int duration) {
                int from = currentTargetOffsetTop;
                int distance = to - from;
                stop();
                if (distance == 0) {
                    return;
                }
                scroller.startScroll(0, 0, 0, distance, duration);
                post(this);
            }
    
            private void stop() {
                removeCallbacks(this);
                if (!scroller.isFinished()) {
                    scroller.forceFinished(true);
                }
                lastY = 0;
            }
        }
    
        /**
         * 在scroll结束的时候会回调这个方法
         *
         * @param isForceFinish 是否是强制结束的
         */
        private void onScrollFinish(boolean isForceFinish) {
            if (isAutoRefresh && !isForceFinish) {
                isAutoRefresh = false;
                changeState(State.LOADING);
                if (refreshListener != null) {
                    refreshListener.onRefresh();
                }
                finishSpinner();
            }
        }
    
        public interface OnRefreshListener {
            void onRefresh();
        }
    
        public enum State {
            RESET, PULL, LOADING, COMPLETE
        }
    
    }
    
    

    还有这个类:

    package com.ccstest.views;
    
    import android.content.Context;
    import android.graphics.Color;
    import android.support.v4.view.MotionEventCompat;
    import android.support.v4.view.ViewCompat;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewConfiguration;
    import android.view.ViewGroup;
    import android.widget.AbsListView;
    import android.widget.ImageView;
    import android.widget.Scroller;
    
    import com.ccstest.R;
    
    /**
     * Created by AItsuki on 2016/6/13.
     * -
     */
    public class RefreshLayout extends ViewGroup {
    
        private static final String TAG = "RefreshLayout";
        private static final float DRAG_RATE = 0.5f;
        private static final int INVALID_POINTER = -1;
    
        // scroller duration
        private static final int SCROLL_TO_TOP_DURATION = 800;
        private static final int SCROLL_TO_REFRESH_DURATION = 250;
        private static final long SHOW_COMPLETED_TIME = 500;
    
        private View refreshHeader;
        private View target;
        private int currentTargetOffsetTop; // target/header偏移距离
        private int lastTargetOffsetTop;
    
        private boolean hasMeasureHeader;   // 是否已经计算头部高度
        private int touchSlop;
        private int headerHeight;       // header高度
        private int totalDragDistance;  // 需要下拉这个距离才进入松手刷新状态,默认和header高度一致
        private int maxDragDistance;
        private int activePointerId;
        private boolean isTouch;
        private boolean hasSendCancelEvent;
        private float lastMotionX;
        private float lastMotionY;
        private float initDownY;
        private float initDownX;
        private static final int START_POSITION = 0;
        private MotionEvent lastEvent;
        private boolean mIsBeginDragged;
        private AutoScroll autoScroll;
        private State state = State.RESET;
        private OnRefreshListener refreshListener;
        private boolean isAutoRefresh;
    
    
        // 刷新成功,显示500ms成功状态再滚动回顶部
        private Runnable delayToScrollTopRunnable = new Runnable() {
            @Override
            public void run() {
                autoScroll.scrollTo(START_POSITION, SCROLL_TO_TOP_DURATION);
            }
        };
    
        private Runnable autoRefreshRunnable = new Runnable() {
            @Override
            public void run() {
                // 标记当前是自动刷新状态,finishScroll调用时需要判断
                // 在actionDown事件中重新标记为false
                isAutoRefresh = true;
                changeState(State.PULL);
                autoScroll.scrollTo(totalDragDistance, SCROLL_TO_REFRESH_DURATION);
            }
        };
    
    
        public RefreshLayout(Context context) {
            this(context, null);
        }
    
        public RefreshLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
            autoScroll = new AutoScroll();
    
            // 添加默认的头部,先简单的用一个ImageView代替头部
            ImageView imageView = new ImageView(context);
            imageView.setImageResource(R.drawable.gold);
            imageView.setBackgroundColor(Color.BLACK);
            setRefreshHeader(imageView);
        }
    
        /**
         * 设置自定义header
         */
        public void setRefreshHeader(View view) {
            if (view != null && view != refreshHeader) {
                removeView(refreshHeader);
    
                // 为header添加默认的layoutParams
                LayoutParams layoutParams = view.getLayoutParams();
                if (layoutParams == null) {
                    layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, Utils.dp2px(getContext(), 80));
    //                layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
                    view.setLayoutParams(layoutParams);
                }
                refreshHeader = view;
                addView(refreshHeader);
            }
        }
    
        public void setRefreshListener(OnRefreshListener refreshListener) {
            this.refreshListener = refreshListener;
        }
    
        public void refreshComplete() {
            changeState(State.COMPLETE);
            // if refresh completed and the target at top, change state to reset.
            if (currentTargetOffsetTop == START_POSITION) {
                changeState(State.RESET);
            } else {
                // waiting for a time to show refreshView completed state.
                // at next touch event, remove this runnable
                if (!isTouch) {
                    postDelayed(delayToScrollTopRunnable, SHOW_COMPLETED_TIME);
                }
            }
        }
    
        public void autoRefresh() {
            autoRefresh(500);
        }
    
        /**
         * 在onCreate中调用autoRefresh,此时View可能还没有初始化好,需要延长一段时间执行。
         *
         * @param duration 延时执行的毫秒值
         */
        public void autoRefresh(long duration) {
            if (state != State.RESET) {
                return;
            }
            postDelayed(autoRefreshRunnable, duration);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            if (target == null) {
                ensureTarget();
            }
    
            if (target == null) {
                return;
            }
    
            // ----- measure target -----
            // target占满整屏
            target.measure(MeasureSpec.makeMeasureSpec(
                    getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                    MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
                    getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY));
    
            // ----- measure refreshView-----
            measureChild(refreshHeader, widthMeasureSpec, heightMeasureSpec);
            if (!hasMeasureHeader) { // 防止header重复测量
                hasMeasureHeader = true;
                headerHeight = refreshHeader.getMeasuredHeight(); // header高度
                totalDragDistance = headerHeight;   // 需要pull这个距离才进入松手刷新状态
                if (maxDragDistance == 0) {  // 默认最大下拉距离为控件高度的五分之四
                    maxDragDistance = totalDragDistance*4/5;
                }
            }
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            final int width = getMeasuredWidth();
            final int height = getMeasuredHeight();
            if (getChildCount() == 0) {
                return;
            }
    
            if (target == null) {
                ensureTarget();
            }
            if (target == null) {
                return;
            }
    
            // target铺满屏幕
            final View child = target;
            final int childLeft = getPaddingLeft();
            final int childTop = getPaddingTop() + currentTargetOffsetTop;
            final int childWidth = width - getPaddingLeft() - getPaddingRight();
            final int childHeight = height - getPaddingTop() - getPaddingBottom();
            child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
    
            // header放到target的上方,水平居中
            int refreshViewWidth = refreshHeader.getMeasuredWidth();
            refreshHeader.layout((width / 2 - refreshViewWidth / 2),
                    -headerHeight + currentTargetOffsetTop,
                    (width / 2 + refreshViewWidth / 2),
                    currentTargetOffsetTop);
        }
    
        /**
         * 将第一个Child作为target
         */
        private void ensureTarget() {
            // Don't bother getting the parent height if the parent hasn't been laid
            // out yet.
            if (target == null) {
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    if (!child.equals(refreshHeader)) {
                        target = child;
                        break;
                    }
                }
            }
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (!isEnabled() || target == null) {
                return super.dispatchTouchEvent(ev);
            }
    
            final int actionMasked = ev.getActionMasked(); // support Multi-touch
            switch (actionMasked) {
                case MotionEvent.ACTION_DOWN:
                    activePointerId = ev.getPointerId(0);
                    isAutoRefresh = false;
                    isTouch = true;
                    hasSendCancelEvent = false;
                    mIsBeginDragged = false;
                    lastTargetOffsetTop = currentTargetOffsetTop;
                    currentTargetOffsetTop = target.getTop();
                    initDownX = lastMotionX = ev.getX(0);
                    initDownY = lastMotionY = ev.getY(0);
                    autoScroll.stop();
                    removeCallbacks(delayToScrollTopRunnable);
                    removeCallbacks(autoRefreshRunnable);
                    super.dispatchTouchEvent(ev);
                    return true;    // return true,否则可能接受不到move和up事件
    
                case MotionEvent.ACTION_MOVE:
                    if (activePointerId == INVALID_POINTER) {
                        Log.e(TAG, "Got ACTION_MOVE event but don't have an active pointer id.");
                        return super.dispatchTouchEvent(ev);
                    }
                    lastEvent = ev;
                    float x = ev.getX(MotionEventCompat.findPointerIndex(ev, activePointerId));
                    float y = ev.getY(MotionEventCompat.findPointerIndex(ev, activePointerId));
                    float yDiff = y - lastMotionY;
                    float offsetY = yDiff * DRAG_RATE;
                    lastMotionX = x;
                    lastMotionY = y;
    
                    if (!mIsBeginDragged && Math.abs(y - initDownY) > touchSlop) {
                        mIsBeginDragged = true;
                    }
    
                    if (mIsBeginDragged) {
                        boolean moveDown = offsetY > 0; // ↓
                        boolean canMoveDown = canChildScrollUp();
                        boolean moveUp = !moveDown;     // ↑
                        boolean canMoveUp = currentTargetOffsetTop > START_POSITION;
    
                        // 判断是否拦截事件
                        if ((moveDown && !canMoveDown) || (moveUp && canMoveUp)) {
                            moveSpinner(offsetY);
                            return true;
                        }
                    }
                    break;
    
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    isTouch = false;
                    if (currentTargetOffsetTop > START_POSITION) {
                        finishSpinner();
                    }
                    activePointerId = INVALID_POINTER;
                    break;
    
                case MotionEvent.ACTION_POINTER_DOWN:
                    int pointerIndex = MotionEventCompat.getActionIndex(ev);
                    if (pointerIndex < 0) {
                        Log.e(TAG, "Got ACTION_POINTER_DOWN event but have an invalid action index.");
                        return super.dispatchTouchEvent(ev);
                    }
                    lastMotionX = ev.getX(pointerIndex);
                    lastMotionY = ev.getY(pointerIndex);
                    lastEvent = ev;
                    activePointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
                    break;
    
                case MotionEvent.ACTION_POINTER_UP:
                    onSecondaryPointerUp(ev);
                    lastMotionY = ev.getY(ev.findPointerIndex(activePointerId));
                    lastMotionX = ev.getX(ev.findPointerIndex(activePointerId));
                    break;
            }
            return super.dispatchTouchEvent(ev);
        }
    
    
        private void moveSpinner(float diff) {
            int offset = Math.round(diff);
            if (offset == 0) {
                return;
            }
    
            // 发送cancel事件给child
            if (!hasSendCancelEvent && isTouch && currentTargetOffsetTop > START_POSITION) {
                sendCancelEvent();
                hasSendCancelEvent = true;
            }
    
            int targetY = Math.max(0, currentTargetOffsetTop + offset); // target不能移动到小于0的位置……
            // y = x - (x/2)^2
            float extraOS = targetY - totalDragDistance;
            float slingshotDist = totalDragDistance;
            float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2) / slingshotDist);
            float tensionPercent = (float) (tensionSlingshotPercent - Math.pow(tensionSlingshotPercent / 2, 2));
    
            if (offset > 0) { // 下拉的时候才添加阻力
                offset = (int) (offset * (1f - tensionPercent));
                targetY = Math.max(0, currentTargetOffsetTop + offset);
            }
    
            // 1. 在RESET状态时,第一次下拉出现header的时候,设置状态变成PULL
            if (state == State.RESET && currentTargetOffsetTop == START_POSITION && targetY > 0) {
                changeState(State.PULL);
            }
    
            // 2. 在PULL或者COMPLETE状态时,header回到顶部的时候,状态变回RESET
            if (currentTargetOffsetTop > START_POSITION && targetY <= START_POSITION) {
                if (state == State.PULL || state == State.COMPLETE) {
                    changeState(State.RESET);
                }
            }
    
            // 3. 如果是从底部回到顶部的过程(往上滚动),并且手指是松开状态, 并且当前是PULL状态,状态变成LOADING,这时候我们需要强制停止autoScroll
            if (state == State.PULL && !isTouch && currentTargetOffsetTop > totalDragDistance && targetY <= totalDragDistance) {
                autoScroll.stop();
                changeState(State.LOADING);
                if (refreshListener != null) {
                    refreshListener.onRefresh();
                }
                // 因为判断条件targetY <= totalDragDistance,会导致不能回到正确的刷新高度(有那么一丁点偏差),调整change
                int adjustOffset = totalDragDistance - targetY;
                offset += adjustOffset;
            }
    
            setTargetOffsetTopAndBottom(offset);
    
            // 别忘了回调header的位置改变方法。
            if (refreshHeader instanceof RefreshHeader) {
                ((RefreshHeader) refreshHeader)
                        .onPositionChange(currentTargetOffsetTop, lastTargetOffsetTop, totalDragDistance, isTouch, state);
    
            }
    
        }
    
        private void finishSpinner() {
            if (state == State.LOADING) {
                if (currentTargetOffsetTop > totalDragDistance) {
                    autoScroll.scrollTo(totalDragDistance, SCROLL_TO_REFRESH_DURATION);
                }
            } else {
                autoScroll.scrollTo(START_POSITION, SCROLL_TO_TOP_DURATION);
            }
        }
    
    
        private void changeState(State state) {
            this.state = state;
    
    //        Toast.makeText(getContext(), state.toString(), Toast.LENGTH_SHORT).show();
            RefreshHeader refreshHeader = this.refreshHeader instanceof RefreshHeader ? ((RefreshHeader) this.refreshHeader) : null;
            if (refreshHeader != null) {
                switch (state) {
                    case RESET:
                        refreshHeader.reset();
                        break;
                    case PULL:
                        refreshHeader.pull();
                        break;
                    case LOADING:
                        refreshHeader.refreshing();
                        break;
                    case COMPLETE:
                        refreshHeader.complete();
                        break;
                }
            }
        }
    
        //add
    
    
    
    
        private void setTargetOffsetTopAndBottom(int offset) {
            if (offset == 0) {
                return;
            }
            target.offsetTopAndBottom(offset);
            refreshHeader.offsetTopAndBottom(offset);
            lastTargetOffsetTop = currentTargetOffsetTop;
            currentTargetOffsetTop = target.getTop();
    //        Log.e(TAG, "moveSpinner: currentTargetOffsetTop = "+ currentTargetOffsetTop);
            invalidate();
        }
    
        private void sendCancelEvent() {
            if (lastEvent == null) {
                return;
            }
            MotionEvent ev = MotionEvent.obtain(lastEvent);
            ev.setAction(MotionEvent.ACTION_CANCEL);
            super.dispatchTouchEvent(ev);
        }
    
        private void onSecondaryPointerUp(MotionEvent ev) {
            final int pointerIndex = MotionEventCompat.getActionIndex(ev);
            final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
            if (pointerId == activePointerId) {
                // This was our active pointer going up. Choose a new
                // active pointer and adjust accordingly.
                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                lastMotionY = ev.getY(newPointerIndex);
                lastMotionX = ev.getX(newPointerIndex);
                activePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
            }
        }
    
        public boolean canChildScrollUp() {
            if (android.os.Build.VERSION.SDK_INT < 14) {
                if (target instanceof AbsListView) {
                    final AbsListView absListView = (AbsListView) target;
                    return absListView.getChildCount() > 0
                            && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
                            .getTop() < absListView.getPaddingTop());
                } else {
                    return ViewCompat.canScrollVertically(target, -1) || target.getScrollY() > 0;
                }
            } else {
                return ViewCompat.canScrollVertically(target, -1);
            }
        }
    
    
        private class AutoScroll implements Runnable {
            private Scroller scroller;
            private int lastY;
    
            public AutoScroll() {
                scroller = new Scroller(getContext());
            }
    
            @Override
            public void run() {
                boolean finished = !scroller.computeScrollOffset() || scroller.isFinished();
                if (!finished) {
                    int currY = scroller.getCurrY();
                    int offset = currY - lastY;
                    lastY = currY;
                    moveSpinner(offset);
                    post(this);
                    onScrollFinish(false);
                } else {
                    stop();
                    onScrollFinish(true);
                }
            }
    
            public void scrollTo(int to, int duration) {
                int from = currentTargetOffsetTop;
                int distance = to - from;
                stop();
                if (distance == 0) {
                    return;
                }
                scroller.startScroll(0, 0, 0, distance, duration);
                post(this);
            }
    
            private void stop() {
                removeCallbacks(this);
                if (!scroller.isFinished()) {
                    scroller.forceFinished(true);
                }
                lastY = 0;
            }
        }
    
        /**
         * 在scroll结束的时候会回调这个方法
         *
         * @param isForceFinish 是否是强制结束的
         */
        private void onScrollFinish(boolean isForceFinish) {
            if (isAutoRefresh && !isForceFinish) {
                isAutoRefresh = false;
                changeState(State.LOADING);
                if (refreshListener != null) {
                    refreshListener.onRefresh();
                }
                finishSpinner();
            }
        }
    
        public interface OnRefreshListener {
            void onRefresh();
        }
    
        public enum State {
            RESET, PULL, LOADING, COMPLETE
        }
    
    }
    
    

    基本下拉OK了.现在在巨人肩膀上我来模仿这个正版的头部和波浪的下拉刷新
    先得来个波浪:
    代码如下:

    package com.ccstest.views;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.DrawFilter;
    import android.graphics.Paint;
    import android.graphics.PaintFlagsDrawFilter;
    import android.graphics.Path;
    import android.util.AttributeSet;
    import android.view.View;
    
    public class WaveView3 extends View {
    
        private Path mAbovePath,mBelowWavePath;
        private Paint mAboveWavePaint,mBelowWavePaint;
    
        private DrawFilter mDrawFilter;
    
        private float φ;
    
        private OnWaveAnimationListener mWaveAnimationListener;
    
        public WaveView3(Context context, AttributeSet attrs) {
            super(context, attrs);
            //初始化路径
            mAbovePath = new Path();
            mBelowWavePath = new Path();
            //初始化上面的画笔
            mAboveWavePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mAboveWavePaint.setAntiAlias(true);
            mAboveWavePaint.setStyle(Paint.Style.FILL);
            mAboveWavePaint.setColor(Color.WHITE);
            //初始化下面的画笔
            mBelowWavePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mBelowWavePaint.setAntiAlias(true);
            mBelowWavePaint.setStyle(Paint.Style.FILL);
            mBelowWavePaint.setColor(Color.WHITE);
            mBelowWavePaint.setAlpha(80);
            //画布抗锯齿
            mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
    
            canvas.setDrawFilter(mDrawFilter);
    
            mAbovePath.reset();//清除画笔中的内容
            mBelowWavePath.reset();
    
            φ-=0.1f;
    
            float y,y2;
    
            double ω = 6*Math.PI / getWidth();
    
            mAbovePath.moveTo(getLeft(),getBottom());
            mBelowWavePath.moveTo(getLeft(),getBottom());
            for (float x = 0; x <= getWidth(); x += 20) {
                /**
                 *  y=Asin(ωx+φ)+k
                 *  A—振幅越大,波形在y轴上最大与最小值的差值越大
                 *  ω—角速度, 控制正弦周期(单位角度内震动的次数)
                 *  φ—初相,反映在坐标系上则为图像的左右移动。这里通过不断改变φ,达到波浪移动效果
                 *  k—偏距,反映在坐标系上则为图像的上移或下移。
                 */
                y = (float) (8 * Math.cos(ω * x + φ) +8);
                y2 = (float) (8 * Math.sin(ω * x + φ));
                mAbovePath.lineTo(x, y);
                mBelowWavePath.lineTo(x, y2);
                //回调 把y坐标的值传出去(在activity里面接收让小机器人随波浪一起摇摆)
    //            mWaveAnimationListener.OnWaveAnimation(y);
            }
            mAbovePath.lineTo(getRight(),getBottom());
            mBelowWavePath.lineTo(getRight(),getBottom());
    
            canvas.drawPath(mAbovePath,mAboveWavePaint);
            canvas.drawPath(mBelowWavePath,mBelowWavePaint);
    
            postInvalidateDelayed(10);
    
        }
    
        public void setOnWaveAnimationListener(OnWaveAnimationListener l){
            this.mWaveAnimationListener = l;
        }
    
        public interface OnWaveAnimationListener{
            void OnWaveAnimation(float y);
        }
    
    }
    

    然后呢,基本上就OK了.
    接下来把布局和activity中的代码贴下:
    布局:

    <?xml version="1.0" encoding="utf-8"?>
    <com.ccstest.views.RefreshLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/sw"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:addStatesFromChildren="true"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        >
    
        <android.support.design.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            tools:context="com.ccstest.activity.ScrollingActivity">
    
            <android.support.design.widget.AppBarLayout
                android:id="@+id/app_bar"
                android:layout_width="match_parent"
                android:layout_height="@dimen/app_bar_height"
                android:fitsSystemWindows="true"
                android:theme="@style/AppTheme.AppBarOverlay">
    
                <android.support.design.widget.CollapsingToolbarLayout
                    android:id="@+id/toolbar_layout"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:background="@drawable/wallet_bg4"
                    android:fitsSystemWindows="true"
                    app:contentScrim="?attr/colorPrimary"
                    app:layout_scrollFlags="scroll|exitUntilCollapsed"
                    >
    
                    <LinearLayout
                        android:id="@+id/llllll"
                        android:layout_width="match_parent"
                        android:layout_height="80dp"
                        android:orientation="horizontal"
                        android:visibility="gone"
                        >
    
                        <ImageView
                            android:layout_width="20dp"
                            android:layout_height="20dp"
                            android:layout_marginLeft="30dp"
                            android:src="@drawable/player_btn_share"
                            />
    
                        <View
                            android:layout_width="0dp"
                            android:layout_height="0dp"
                            android:layout_weight="1"/>
    
                        <ImageView
                            android:layout_width="20dp"
                            android:layout_height="20dp"
                            android:layout_marginRight="30dp"
                            android:src="@drawable/player_btn_share"
                            />
                    </LinearLayout>
    
                    <ImageView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="left|center"
                        android:layout_marginLeft="30dp"
                        android:src="@drawable/icon_heart"
                        />
    
                    <ImageView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentRight="true"
                        android:layout_centerVertical="true"
                        android:layout_gravity="right|center"
                        android:layout_marginRight="30dp"
                        android:src="@drawable/icon_heart"
                        />
    
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center|bottom"
                        android:layout_marginBottom="10dp"
                        android:text="注册/登录"
                        android:textSize="16sp"
                        />
    
                    <android.support.v7.widget.Toolbar
                        android:id="@+id/toolbar"
                        android:layout_width="match_parent"
                        android:layout_height="?attr/actionBarSize"
                        app:layout_collapseMode="pin"
                        app:popupTheme="@style/AppTheme.PopupOverlay">
    
                        <RelativeLayout
                            android:layout_width="match_parent"
                            android:layout_height="match_parent">
    
                        </RelativeLayout>
                    </android.support.v7.widget.Toolbar>
    
                    <com.sloydev.collapsingavatartoolbar.CollapsingAvatarToolbar
                        android:id="@+id/avatartoolbar"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        app:collapsedImageSize="30dp"
                        app:collapsedPadding="2dp"
                        app:expandedImageSize="60dp"
                        app:expandedPadding="2dp"
                        >
    
                        <LinearLayout
                            android:id="@+id/ll"
                            android:layout_width="match_parent"
                            android:layout_height="match_parent"
                            android:gravity="center_horizontal"
                            android:orientation="horizontal">
    
                            <de.hdodenhof.circleimageview.CircleImageView
                                android:id="@id/cat_avatar"
                                android:layout_width="60dp"
                                android:layout_height="60dp"
                                android:layout_marginBottom="12dp"
                                android:src="@drawable/icon"
                                app:border_color="#fff"
                                app:border_width="2dp"
                                />
    
                            <TextView
                                android:id="@id/cat_title"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text=""
                                />
                        </LinearLayout>
    
                    </com.sloydev.collapsingavatartoolbar.CollapsingAvatarToolbar>
    
                    <com.ccstest.views.WaveView3
                        android:id="@+id/wave_view"
                        android:layout_width="match_parent"
                        android:layout_height="10dp"
                        android:layout_gravity="bottom"
                        android:visibility="gone"
                        />
                </android.support.design.widget.CollapsingToolbarLayout>
            </android.support.design.widget.AppBarLayout>
    
            <include layout="@layout/content_scrolling"/>
    
        </android.support.design.widget.CoordinatorLayout>
    </com.ccstest.views.RefreshLayout>
    

    哎,简书说我文章太长了,需要源码的同志们点赞后留下你们的邮箱,
    传送门

    相关文章

      网友评论

      本文标题:CoordinatorLayout自定义下拉刷新

      本文链接:https://www.haomeiwen.com/subject/dbkrbttx.html