Android_滑动的时候头部变化效果

废话少说,直接上图哦

先看看上面的布局。从上到下:头部使用相对布局,然后是三个选项。然后线性布局里面装的fragment,fragment里面又是能够上啦刷新的listview。我的思路是准备在activity里面直接实现。

activtiy里面有两个处理事件的方法

onTouchEvent(MotionEvent event)
dispatchTouchEvent(MotionEvent ev)

这两个事件选哪个合适呢?肯定是选第二个赛。选第一个的话肯定实现不起效果。由于事件传递(不懂百度),就被listview消耗了。

以下进入正题:

第一步:滚动的时候试着改变头部的高度

第二步:逐渐让周围不须要的字体消失

第三步:依据偏移率计算偏移量。然后把图片和须要的文字移动到顶端中心处

先声明须要用到的数据,以下所有列举出来

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距离

/**
 * 初始化须要滚动的距离
 */
private void initDistance() {
    mOrignHight = rl_head.getLayoutParams().height;
    mMinHight = UIUtils.dip2px(this, 45);  //设置最小的高度为这么多
    mNeedDistance = mOrignHight - mMinHight;
    RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) img_head_portrait.getLayoutParams();
    mPhotoOriginHeight = params.height;
    mPhotoOriginWidth = params.width;
    mPhotoLeft = params.leftMargin;
    mPhotoTop = params.topMargin;
    mPhotoNeedMoveDistanceX = UIUtils.getWindowWidth(this) / 2 - mPhotoLeft - mMinHight;
    mPhotoNeedMoveDistanceY = mPhotoTop - UIUtils.dip2px(this, 10);
    /*******************移动的文字初始化***************************/
    RelativeLayout.LayoutParams textParams = (RelativeLayout.LayoutParams) tv_user_name.getLayoutParams();
    mTextLeft = textParams.leftMargin;
    mTextTop = textParams.topMargin;
    mTextNeedMoveDistanceX = UIUtils.getWindowWidth(this) / 2 - mTextLeft + 10;
    mTextNeedMoveDistanceY = mTextTop - mMinHight / 2 / 2;  //这里计算有点误差,正确的应该是剪去获取textview高度的一半
}

实现第一步操作:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mLastY = (int) ev.getY();
            LogUtils.d(TAG, "ACTION_MOVE==mCurrentDistance" + mCurrentDistance);
            return super.dispatchTouchEvent(ev); //传递事件 比如能够用来子view的点击事件等
        case MotionEvent.ACTION_MOVE:
            int y = (int) ev.getY();
            int dy = mLastY - y;
            LogUtils.d(TAG, "ACTION_MOVE==mCurrentDistance" + mCurrentDistance);
            if (mCurrentDistance >= mNeedDistance && dy > 0) {
                return super.dispatchTouchEvent(ev);  //传递事件
            }
            if (mCurrentDistance <= 0 && dy < 0) {
                return super.dispatchTouchEvent(ev); //把事件传递进去
            }
            //改变布局
            changeTheLayout(dy);  
            mLastY = y;
            break;
        case MotionEvent.ACTION_UP:
            checkTheHeight();
            LogUtils.d(TAG, "ACTION_MOVE==mCurrentDistance" + mCurrentDistance);
            return super.dispatchTouchEvent(ev);
    }

    return false;
}

改变布局的大小的:

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

获取了偏移率(0.0-1.0),然后就能够依据这个改变透明度了和大小了。

/**
 * 依据变化率来改变这些这些控件的变化率位置
 *
 * @param rate
 */
private void changeTheAlphaAndPostion(float rate) {
    //先改变一些控件的透明度
    if (rate >= 1) {
        tv_user_hosipital.setVisibility(View.GONE);
        tv_user_hosipital_level.setVisibility(View.GONE);
        tv_user_project.setVisibility(View.GONE);
    } else {
        tv_user_hosipital.setVisibility(View.VISIBLE);
        tv_user_hosipital_level.setVisibility(View.VISIBLE);
        tv_user_project.setVisibility(View.VISIBLE);
        tv_user_hosipital.setAlpha(1 - rate);
        tv_user_hosipital_level.setAlpha(1 - rate);
        tv_user_project.setAlpha(1 - rate);
        tv_user_hosipital.setScaleY(1 - rate);
        tv_user_hosipital.setScaleX(1 - rate);
        tv_user_hosipital_level.setScaleY(1 - rate);
        tv_user_hosipital_level.setScaleX(1 - rate);
        tv_user_project.setScaleY(1 - rate);
        tv_user_project.setScaleX(1 - rate);
    }
    //接下来是改变控件的大小和位置了  (这就是关键了)
    final RelativeLayout.LayoutParams photoParams = (RelativeLayout.LayoutParams) img_head_portrait.getLayoutParams();
    photoParams.width = (int) (mPhotoOriginWidth - (rate * (mPhotoOriginWidth - mMinHight - UIUtils.dip2px(this, 10))));
    photoParams.height = (int) (mPhotoOriginWidth - (rate * (mPhotoOriginWidth - mMinHight - UIUtils.dip2px(this, 10))));
    photoParams.leftMargin = (int) (mPhotoLeft + mPhotoNeedMoveDistanceX * rate);
    photoParams.topMargin = (int) (mPhotoTop - mPhotoNeedMoveDistanceY * rate);
    LogUtils.d(TAG, "photoParams.leftMargin" + photoParams.leftMargin);
    LogUtils.d(TAG, " photoParams.topMargin" + photoParams.topMargin);
    img_head_portrait.setLayoutParams(photoParams);
    /*********************文字设置****************************/
    final RelativeLayout.LayoutParams textParams = (RelativeLayout.LayoutParams) tv_user_name.getLayoutParams();
    textParams.leftMargin = (int) (mTextLeft + mTextNeedMoveDistanceX * rate);
    textParams.topMargin = (int) (mTextTop - mTextNeedMoveDistanceY * rate);
    LogUtils.d(TAG, "textParams.leftMargin" + textParams.leftMargin);
    LogUtils.d(TAG, " textParams.topMargin" + textParams.topMargin);
    tv_user_name.setLayoutParams(textParams);
}

卧槽,搞忘了一个方法,滑动的时候须要检查边界值;这里写了一个方法:

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

}

另一个关键的地方,布局文件的时候 最好是图片控件和须要移动的文字textview不要依赖于其它控件,不然计算margin非常麻烦,所以我布局文件就用了简单粗糙的来实现布局:

须要变化的图片控件
<com.facebook.drawee.view.SimpleDraweeView
    android:id="@+id/img_head_portrait"
    android:layout_width="90dp"
    android:layout_height="90dp"
    android:layout_centerVertical="true"
    android:layout_marginLeft="@dimen/margin_20dp"
    android:padding="1dp"
    fresco:fadeDuration="500"
    fresco:placeholderImage="@mipmap/icon_user_avatar" />

须要变化的文字控件
<TextView
    android:id="@+id/tv_user_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="120dp"  //这个距离直接设置的 ,不要用align。。。这个属性,不然不好计算margin
    android:layout_marginTop="@dimen/margin_30dp"  //这个也是一样的道理
    android:text="蒋心平"
    android:textColor="@color/white"
    android:textSize="@dimen/text_size_18" />

几乎相同就这样就能实现上面的效果了。当做完这个功能的时候,我突然认为一些主流app。实现的向上滑动,title慢慢消失或者隐藏就so easy了,原理都是一样的。

仅仅要对事件分发拦截机制有一点认识都能实现这种效果了。


posted @ 2017-07-18 13:34  jzdwajue  阅读(366)  评论(0编辑  收藏  举报