Android滑动改变头部效果

滑动改变头部效果

本篇文章基于以下博客,谢谢作者的无私付出和奉献
http://blog.csdn.net/qiuchunjia/article/details/51094667

  • 在原篇的基础上进行加工
  • 添加回弹效果
  • 处理一些基本的bug
  • 为和我遇到同样问题不得解的的人

效果图:

https://github.com/Wwq-Scofield/CodeReview/blob/master/demo1.gif

知识点

需要了解的事件分发,事件传递,自定义view(ViewGroup),VelocityTracker以及动画的基本知识
本篇文章和上篇作者的做法不一样,是自定义了RelativeLayout的方式进行事件拦截,事件分发,以免activity看起来复杂。

/**
 * Created by wwq on 2017/10/6.
 * 第一步:滚动的时候试着改变头部的高度
 * 第二步:逐渐让周围不需要的字体消失
 * 第三步:根据偏移率计算偏移量,然后把图片和需要的文字移动到顶端中心处
 */

public class SwipeView extends RelativeLayout {
private static final String TAG = "wwq";
private RelativeLayout rlHeader;
private TextView tvName, tvSchool;
private CircleImageView ivHeader; 
/**
 * 几个状态
 */
public static int TOP=0;//顶部
public static int BOTTOM=1;//底部
public int currentState=BOTTOM;//当前状态
/**
 * 变量
 *
 * @param context
 */
private int mLastY = 0;  //最后的点
private static int mNeedDistance;   // 需要滑动的距离
private static int mMinHight; //最小高度
private static int mOrignHight; //原始的高度

private int mCurrentDistance = 0;  //当前的距离
private float mRate = 0;  //距离与目标距离的变化率                              
//mRate=mCurrentDistance/mNeedDistance
private int mPhotoOriginHeight; //图片的原始高度
private int mPhotoOriginWidth; //图片的原始宽度
private int mPhotoLeft;  //图片距左边的距离
private int mPhotoTop;  //图片距离上边的距离
private int mPhotoNeedMoveDistanceX;  // 图片需要移动的X距离
private int mPhotoNeedMoveDistanceY;  // 图片需要移动的Y距离
//需要移动的文字
private int mTextLeft;  //文字距左边的距离
private int mTextTop;  //文字距离上边的距离
private int mTextNeedMoveDistanceX;  // 文字需要移动的X距离
private int mTextNeedMoveDistanceY;  //文字需要移动的Y距离


/**
 * /**
 * 初始化需要滚动的距离
 */


public SwipeView(Context context) {
    super(context, null);
}

public SwipeView(Context context, AttributeSet attrs) {
    super(context, attrs, 0);
}

public SwipeView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

private void init() {
    mOrignHight = rlHeader.getLayoutParams().height;
    mMinHight = UIUtils.dip2px(getContext(), 50);  //设置最小的高度为这么多
    mNeedDistance = mOrignHight - mMinHight;
    RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) ivHeader.getLayoutParams();
    mPhotoOriginHeight = params.height;
    mPhotoOriginWidth = params.width;
    mPhotoLeft = params.leftMargin;
    mPhotoTop = params.topMargin;
    mPhotoNeedMoveDistanceX = UIUtils.getWindowWidth(getContext()) / 2 - mPhotoLeft - mMinHight;
    mPhotoNeedMoveDistanceY = mPhotoTop - UIUtils.dip2px(getContext(),5);
    /*******************移动的文字初始化***************************/
    RelativeLayout.LayoutParams textParams = (RelativeLayout.LayoutParams) tvName.getLayoutParams();
    mTextLeft = textParams.leftMargin;
    mTextTop = textParams.topMargin;
    mTextNeedMoveDistanceX = UIUtils.getWindowWidth(getContext()) / 2 - mTextLeft + 10;
    mTextNeedMoveDistanceY = mTextTop - textParams.height-mMinHight/4;  //这里已改成剪去获取textview高度的

    Log.d("wwq","mNeedDistance= "+mNeedDistance);
}
RecyclerView recyclerView;

@Override
protected void onFinishInflate() {
    super.onFinishInflate();
    rlHeader = (RelativeLayout) findViewById(R.id.rl_header);
    ivHeader = (CircleImageView) findViewById(R.id.iv_header);
    tvName = (TextView) findViewById(R.id.tv_name);
    tvSchool = (TextView) findViewById(R.id.tv_school);
    rlHeader.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            init();
            rlHeader.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
    });
    recyclerView= (RecyclerView) findViewById(R.id.recleview);
}

@RequiresApi(api = Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    if(currentState==TOP &&recyclerView.canScrollVertically(-1)){
        return false;
    }
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mLastY = (int) ev.getY();
            tempY= (int) ev.getY();
            Log.d(TAG, "ACTION_DOWN==mCurrentDistance" + mCurrentDistance);
            break;//传递事件 例如可以用来子view的点击事件等
        case MotionEvent.ACTION_MOVE:
            int y = (int) ev.getY();
            int dy = mLastY - y;
            Log.d("wwq", "dy: " + dy);
            Log.d(TAG, "ACTION_MOVE==mCurrentDistance" + mCurrentDistance);
            if (mCurrentDistance >= mNeedDistance && dy >= 0) {
                Log.d("wwq", "event false 1");
                return false;  //传递事件
            }
            if (mCurrentDistance <= 0 && dy <= 0) {
                Log.d("wwq", "event false 2"); 
                return false; //把事件传递进去
            }
            Log.d("wwq","------------------");
              return true;
        case MotionEvent.ACTION_UP: 
            Log.d(TAG, "ACTION_UP==mCurrentDistance" + mCurrentDistance);
            return false;
    }

    return super.onInterceptTouchEvent(ev);

} 
private int tempY;
private boolean isUp;

private VelocityTracker mVelocityTracker;
@Override
public boolean onTouchEvent(MotionEvent ev) {
    if (mVelocityTracker == null) {
        mVelocityTracker = VelocityTracker.obtain();
    }
    mVelocityTracker.addMovement(ev);

    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mLastY = (int) ev.getY(); 
            break; //传递事件 例如可以用来子view的点击事件等
        case MotionEvent.ACTION_MOVE:
            int distance=((int)getY()-tempY);
            Log.d("wwq","=====  "+distance);
            int y = (int) ev.getY();
            int dy = mLastY - y;

            if (mCurrentDistance >= mNeedDistance && dy > 0) { 
                return false;  //传递事件
            }
            if (mCurrentDistance <= 0 && dy < 0) { 
                return false; //把事件传递进去
            }  
            changeTheLayout(dy);
            mLastY = y;
            return true;
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            if (mVelocityTracker != null) {
                final VelocityTracker velocityTracker = mVelocityTracker;
                velocityTracker.computeCurrentVelocity(1000);
                int velocityY = (int) velocityTracker.getYVelocity();
                if (velocityY > 400){//下划
                    startToTop(0,mOrignHight);
                } else if (velocityY < -400){//上划
                    startToTop(1,mMinHight);
                } else { 
                    tempY=0;
                    if(mCurrentDistance>mNeedDistance/2){
                        Log.d("wwq","> 1/2");
                        startToTop(1,mMinHight);
                    }else {
                        Log.d("wwq","< 1/2");
                        startToTop(0,mOrignHight);
                    }
                }
                mVelocityTracker.recycle();
                mVelocityTracker = null;

            }

            Log.d(TAG, "ACTION_UP==mCurrentDistance" + mCurrentDistance);
            break;
    }
    return false;

}

/**
 * 通过滑动的偏移量
 *
 * @param dy
 */
private void changeTheLayout(int dy) {
    final ViewGroup.LayoutParams layoutParams = rlHeader.getLayoutParams();
    layoutParams.height = layoutParams.height - dy;
    rlHeader.setLayoutParams(layoutParams);
    checkTheHeight();
    rlHeader.requestLayout();
    //计算当前移动了多少距离
    mCurrentDistance = mOrignHight - rlHeader.getLayoutParams().height;
    mRate = (float) (mCurrentDistance * 1.0 / mNeedDistance);
    Log.d("wwq","rate: "+mRate);
    changeTheAlphaAndPostion(mRate);  //获取偏移率然后改变某些控件的透明度,和位置
    Log.d(TAG, "ACTION_MOVE==dy" + dy);
}
//获取了偏移率(0.0-1.0),然后就可以根据这个改变透明度了和大小了。

/**
 *   根据变化率来改变这些这些控件的变化率位置
 *
 *   @param rate
 */
private void changeTheAlphaAndPostion(float rate) {
    //先改变一些控件的透明度
    if (rate >= 1) {
    } else if(0.0!=rate) {
        tvSchool.setAlpha(1 - rate*2);
    }

    //接下来是改变控件的大小和位置了  (这就是关键了)
    final RelativeLayout.LayoutParams photoParams = (RelativeLayout.LayoutParams) ivHeader.getLayoutParams();
    photoParams.width = (int) (mPhotoOriginWidth - (rate * (mPhotoOriginWidth   - UIUtils.dip2px(getContext(), 40))));
    photoParams.height = (int) (mPhotoOriginWidth - (rate * (mPhotoOriginWidth  - UIUtils.dip2px(getContext(), 40))));
    photoParams.leftMargin = (int) (mPhotoLeft + mPhotoNeedMoveDistanceX * rate);
    photoParams.topMargin = (int) (mPhotoTop - mPhotoNeedMoveDistanceY * rate);
    Log.d(TAG, "photoParams.leftMargin" + photoParams.leftMargin);
    Log.d(TAG, " photoParams.topMargin" + photoParams.topMargin);
    ivHeader.setLayoutParams(photoParams);
    /*********************文字设置****************************/
    final RelativeLayout.LayoutParams textParams = (RelativeLayout.LayoutParams) tvName.getLayoutParams();
    textParams.leftMargin = (int) (mTextLeft + mTextNeedMoveDistanceX * rate);
    textParams.topMargin = (int) (mTextTop - mTextNeedMoveDistanceY * rate);
    Log.d(TAG, "textParams.leftMargin" + textParams.leftMargin);
    Log.d(TAG, " textParams.topMargin" + textParams.topMargin);
    tvName.setLayoutParams(textParams);
}
//卧槽,搞忘了一个方法,滑动的时候需要检查边界值;这里写了一个方法:

/**
 * 检查上边界和下边界
 */
private void checkTheHeight() {
    final ViewGroup.LayoutParams layoutParams = rlHeader.getLayoutParams();
    if (layoutParams.height < mMinHight) {
        layoutParams.height = mMinHight;
        rlHeader.setLayoutParams(layoutParams);
        rlHeader.requestLayout();
    }
    if (layoutParams.height > mOrignHight) {
        layoutParams.height = mOrignHight;
        rlHeader.setLayoutParams(layoutParams);
        rlHeader.requestLayout();
    }

}


private void startToTop(float rate,int height){

    Log.d("wwq","startTo--   "+rate);
    if(rate==1){
        mCurrentDistance=mNeedDistance; 
        currentState=TOP;

    }else{ 
        mCurrentDistance=0;
        currentState=BOTTOM;
    }

    final RelativeLayout.LayoutParams rlParams= (RelativeLayout.LayoutParams) rlHeader.getLayoutParams();
    final RelativeLayout.LayoutParams ivParams = (RelativeLayout.LayoutParams) ivHeader.getLayoutParams();
    final RelativeLayout.LayoutParams tvNameParams = (RelativeLayout.LayoutParams) tvName.getLayoutParams();
    RelativeLayout.LayoutParams tvSchoolParams= (RelativeLayout.LayoutParams) tvSchool.getLayoutParams();
    final ValueAnimator rlHAnim=ValueAnimator.ofFloat(rlParams.height,height).setDuration(200);
    rlHAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float animatedValue = (float) animation.getAnimatedValue();
            rlParams.height= (int) animatedValue;
            rlHeader.setLayoutParams(rlParams);
        }
    });

    ValueAnimator ivLeftAnim= ValueAnimator.ofFloat(ivParams.leftMargin,mPhotoLeft + mPhotoNeedMoveDistanceX * rate).setDuration(200);
    ivLeftAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value= (float) animation.getAnimatedValue();
            ivParams.leftMargin= (int) value;
            ivHeader.setLayoutParams(ivParams);
        }
    });
   ValueAnimator ivTopAnim=ValueAnimator.ofFloat(ivParams.topMargin,mPhotoTop - mPhotoNeedMoveDistanceY * rate).setDuration(200);
    ivTopAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value= (float) animation.getAnimatedValue();
            ivParams.topMargin= (int) value;
            ivHeader.setLayoutParams(ivParams);

        }
    });

    ValueAnimator ivScaleAnim=ValueAnimator.ofFloat(ivParams.height,mPhotoOriginWidth - (rate * (mPhotoOriginWidth - UIUtils.dip2px(getContext(), 40)))).setDuration(200);
    ivScaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value= (float) animation.getAnimatedValue();
            ivParams.width= (int) value;
            ivParams.height= (int) value;
            ivHeader.setLayoutParams(ivParams);
        }
    });


    final ValueAnimator tvNameLeftAnim=ValueAnimator.ofFloat(tvNameParams.leftMargin,mTextLeft + mTextNeedMoveDistanceX * rate).setDuration(200);
    tvNameLeftAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value= (float) animation.getAnimatedValue();
            tvNameParams.leftMargin= (int) value;
            tvName.setLayoutParams(tvNameParams);
        }
    });

    ValueAnimator tvNameTopAnim=ValueAnimator.ofFloat(tvNameParams.topMargin,mTextTop - mTextNeedMoveDistanceY * rate).setDuration(200);
    tvNameTopAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value= (float) animation.getAnimatedValue();
            tvNameParams.topMargin= (int) value;
            tvName.setLayoutParams(tvNameParams);
        }
    });

    ValueAnimator tvSchoolAlphaAnim =ValueAnimator.ofFloat(tvSchool.getAlpha()*2,rate==1?0:1).setDuration(200);
    tvSchoolAlphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float value= (float) animation.getAnimatedValue();
            tvSchool.setAlpha(value);
        }
    });
    tvSchoolAlphaAnim.start();

    ivTopAnim.start();
    rlHAnim.start();
    ivLeftAnim.start();
    tvNameTopAnim.start();
    tvNameLeftAnim.start();
    ivScaleAnim.start();
} 

以下是xml布局,正如上篇文章说的,头部几个view的位置距离最好是写成定值,通过调试分配距离,适配还是可以的,



 
    
    



    

转载请注明出处 http://blog.csdn.net/wwq006/article/details/78177325,谢谢。
里面注释写的很清楚,大家应该完全看的懂,有不懂的可以私信提出,交流是技术进步的一种方式

要是觉得有帮助于大家,还望顶下,给其他人也提供到方便

你可能感兴趣的:(android,自定义控件)