仿简书发现页面头部透明度渐变及搜索框动态效果

最近看到简书最版的发现页面的头部动态效果不错,我就想尝试实现以下:先看实现的效果图:

效果图.gif
需求分析

当整个页面的轮播图部分上划消失的过程中,透明度会出现渐变的效果,同时当全部消失或者全部显示的时候搜索框宽度动态变化。

思路

从布局看,最外层是Scrollview和头部包含搜索框的布局,Scrollview里面包裹一个线性布局上下结构的布局,上面可以是ViewPager,下是滑动的Listview或者RecyclerView。
1,布局如下:
为了简单我轮播图我直接用了一个图片代替

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<RelativeLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
<!--自定义的ScrollView 包裹,轮播图和滑动列表-->
    <Myview.MyScrollView
        android:id="@+id/myScrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
        <!--轮播图-->
            <RelativeLayout
                android:id="@+id/rlayout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:background="@color/colorPrimary">
                <ImageView
                    android:layout_width="match_parent"
                    android:layout_height="200dp"
                    android:scaleType="centerCrop"
                    android:src="@drawable/wepp" />
            </RelativeLayout>
            <android.support.v7.widget.RecyclerView
                android:id="@+id/recyclerview"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"></android.support.v7.widget.RecyclerView>

            </LinearLayout>
    </Myview.MyScrollView>

    <!--承载搜索框的布局,是透明度渐变和搜索框动态变化的布局-->
    <RelativeLayout
        android:id="@+id/search"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="#ffffff"
        android:orientation="vertical"
        android:visibility="visible">

        <EditText
            android:id="@+id/search_edit"
            android:layout_width="100dp"
            android:layout_height="30dip"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="10dp"
            android:background="@drawable/serch"
            android:drawableLeft="@mipmap/search"
            android:hint="搜索"
            android:padding="5dip"
            android:singleLine="true"
            android:textColorHint="#AAAAAA"
            android:textSize="14dip" />
    </RelativeLayout>

</RelativeLayout>
</LinearLayout>

2,自定义的MyScrollView:
分析,为啥要自定义呢,为了获取滑动的Y轴的距离,以此来和上面布局的承载轮播图的rlayout布局的底部距屏幕上边距进行比较,自定义的MyScrollView重写onScrollChanged这个方法,然后加个回调方法OnScrollListener。就可以在Activity或者Fragment中动态获取到滑动的距离Y了

public class MyScrollView extends ScrollView {
private OnScrollListener onScrollListener;


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

public void setOnScrollListener(OnScrollListener scrollListener) {
    this.onScrollListener = scrollListener;
}


@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    super.onScrollChanged(l, t, oldl, oldt);
    if (onScrollListener!=null){
        onScrollListener.onScrollChanged(l,t,oldl,oldt);
    }
}

public interface OnScrollListener {
     void onScrollChanged(int x, int y, int oldX, int oldY);
   
}

  }

3,看在Activity中的处理
Activty 继承 MyScrollView.OnScrollListener,重写onScrollChanged(int x, int y, int oldX, int oldY);此方法

public public class MainActivity extends BaseActivity implements MyScrollView.OnScrollListener{
private EditText search_edit;
private MyScrollView myScrollView;
private int searchLayoutTop;//
RelativeLayout search;
RelativeLayout rlayout;
private int rlayoutwidth;
prvitate List<String> list;//假的数据源
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 search_edit = (EditText) findViewById(R.id.search_edit);
    myScrollView =    (MyScrollView)findViewById(R.id.myScrollView);
    search = (RelativeLayout) findViewById(R.id.search);
    rlayout = (RelativeLayout) findViewById(R.id.rlayout);
    recyclerview = (RecyclerView)        findViewById(R.id.recyclerview);
    myScrollView.setOnScrollListener(this);
    list = new ArrayList<>();
    search.getBackground().setAlpha(0);
    for (int i = 0; i < 100; i++) {
        list.add("第" + i + "条数据");
    }
 recyclerview . recyclerview.setLayoutManager(new   LinearLayoutManager(getActivity()));
    recyclerview.setAdapter()//适配器我就不写了,大家自己实现
   }
 //这个方法可以在一开始启动时就获取控件的高度和宽度,如果直 
 接在onCreate()的方法中获取不到
 @Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (!hasFocus){
        searchLayoutTop = rlayout.getBottom();//获取rlayout的布局的底部相对于整个布局的高度

        rlayoutwidth = rlayout.getMeasuredWidth();//rlayout布局的宽度
    }
}

  @Override
public void onScrollChanged(int x, int y, int oldX, int oldY) {
    //变化率
    float headHeight = rlayout.getMeasuredHeight()
            - search.getMeasuredHeight();
    int alpha = (int) (((float) y / headHeight) * 255);//透明度变化速率
    ViewGroup.LayoutParams lp = search_edit.getLayoutParams();
    if (alpha >= 255){
        alpha = 255;
        for (int i = 0; i < rlayoutwidth; i++) {
            lp.width += i/20;
        }
    }
    if (alpha <= 10){
        alpha = 0;
        for (int i = rlayoutwidth; i > 0; i--) {
            lp.width -= i /20;
        }
    }
    search.getBackground().setAlpha(alpha);
    if (lp.width >= rlayoutwidth)
        lp.width = rlayoutwidth;
    if (lp.width <= 200)
        lp.width = 200;
    search_edit.setLayoutParams(lp);
}
}
重点分析onScrollChanged()方法
   @Override
public void onScrollChanged(int x, int y, int oldX, int oldY) {
    //变化率
    float headHeight = rlayout.getMeasuredHeight()
            - search.getMeasuredHeight();
    int alpha = (int) (((float) y / headHeight) * 255);//透明度变化速率
  ViewGroup.LayoutParams lp = search_edit.getLayoutParams();
    if (alpha >= 255){
        alpha = 255;
        for (int i = 0; i < rlayoutwidth; i++) {
            lp.width += i/20;
        }
    }
    if (alpha <= 10){
        alpha = 0;
        for (int i = rlayoutwidth; i > 0; i--) {
            lp.width -= i /20;
        }
    }
    search.getBackground().setAlpha(alpha);//设置透明度
    if (lp.width >= rlayoutwidth)
        lp.width = rlayoutwidth;
    if (lp.width <= 200)
        lp.width = 200;
    search_edit.setLayoutParams(lp);//设置搜索框的宽度
}

y可以自页面滑动的时候获取到,
透明度渐变和搜索框动态变化的依据标准是:

 float headHeight = rlayout.getMeasuredHeight() - 
 search.getMeasuredHeight();//变化的距离
 int alpha = (int) (((float) y / headHeight) * 255);//透明度变化速率

什么意思呢:
我们想想,当前滚动的ScrollView的Y值去减去这个headHeight,这个相差值只允许他在headHeight的高度去变化,如果ScrollView滑动的距离超出这个高度的话,那么我们直接设置alpha为255直接显示变透明,如果这个ScrollView滑动的到顶部时,我们直接设置alpha为0为透明,我们首先拿到这个变化的距离,然后去除以这个headHeight高度拿到这个百分比,然后去乘上255拿到同等比例中的透明度的值,记得这个数值都得是float类型,高度也是,要是int的话这样相除的话就变成0了。

有人疑问了这只是透明度的变化为搜索框的变化也要依据这个呢

请看:
此方法是在代码中动态设置组件的宽度的方法

ViewGroup.LayoutParams lp = search_edit.getLayoutParams();
 lp.width =10;
search_edit.setLayoutParams(lp)

看注释:

 ViewGroup.LayoutParams lp = search_edit.getLayoutParams();

    if (alpha >= 255){
//这个执行时正是rlayout布局即轮播图父布局
 全部隐藏的时候,当然这个时候搜索框是要进行变化的
        alpha = 255;
    //这个循环的方法是动态搜索框的关键,可以重新改变搜索框的 
 变化的速率
          for (int i = 0; i < rlayoutwidth; i++) {
            lp.width += i/20;
        }
    }
    if (alpha <= 10){
//这个执行时正是rlayout布局即轮播图
 父布局全部显示的时候,当然这个时候搜索框也是要进行变化的
        alpha = 0;
   //这个循环的方法是动态搜索框的关键,可以重新改变搜索框的变化的速率
        for (int i = rlayoutwidth; i > 0; i--) {
            lp.width -= i /20;
        }
    }
    search.getBackground().setAlpha(alpha);//设置透明度
    if (lp.width >= rlayoutwidth)
        lp.width = rlayoutwidth;
    if (lp.width <= 200)
        lp.width = 200;
    search_edit.setLayoutParams(lp);//设置搜索框的宽度

好了,以上的思路大概就是这样的,至于文章中的代码,有些不全,因为我是在Fragment中搞的,文章中为了通俗一点我直接手敲的MainActivity 。
注意在Fragment中没有这个方法onWindowFocusChanged(),但是也有一个方法可以获取到组件距离屏幕的距离,宽高等,请看:

Observer  observer = rlayout.getViewTreeObserver();
    observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            searchLayoutTop = rlayout.getBottom();//获取rlayout的布局的底部相对于整个布局的高度
            searchwidth = search_edit.getMeasuredWidth();
            rlayoutwidth = rlayout.getMeasuredWidth();
            return true;
        }
    });
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,786评论 25 709
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,262评论 4 61
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,825评论 1 92
  • 下班回到家,我还没来得及放下手里的东西,竹子就站在沙发上,瞪着眼睛,向我挥着胳膊,高声宣布:"妈妈,知道吗,...
    盈洋洋阅读 302评论 0 1
  • 谁还相信花朵是夜晚不眠的歌者星子是天堂里亮着的灯火谁还愿倾听河流在大地上悄悄走过月光在草叶上跳着舞蹈 午夜睡醒的时...
    竹无心a阅读 932评论 19 37