AndroidArticleMD自定义View

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

2017-03-01  本文已影响1435人  奋斗的犇犇

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

效果图.gif
需求分析

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

思路

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

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

}
</pre>
3,看在Activity中的处理
Activty 继承 MyScrollView.OnScrollListener,重写onScrollChanged(int x, int y, int oldX, int oldY);此方法
<pre>
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);
}
}
</pre>

重点分析onScrollChanged()方法

<pre>
@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);//设置搜索框的宽度
}
</pre>

y可以自页面滑动的时候获取到,
透明度渐变和搜索框动态变化的依据标准是:
<pre>float headHeight = rlayout.getMeasuredHeight() - search.getMeasuredHeight();//变化的距离
int alpha = (int) (((float) y / headHeight) * 255);//透明度变化速率
</pre>

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

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

请看:
此方法是在代码中动态设置组件的宽度的方法
<pre>
ViewGroup.LayoutParams lp = search_edit.getLayoutParams();
lp.width =10;
search_edit.setLayoutParams(lp)
</pre>
看注释:
<pre>
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);//设置搜索框的宽度
</pre>

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

上一篇下一篇

猜你喜欢

热点阅读