下面是一个简单的仿qq空间标题变色的效果:
实现思路:根布局是一个RelativeLayout,在RelativeLayout里面放一个ScrollView和LinearLayout(标题布局),将标题布局设置为透明,监听ScrollView的滑动,根据滑动的距离计算出alpha值并设置给标题布局;
1、将标题布局设置为透明
//设置为透明
titleBar.getBackground().setAlpha(0);
在将标题布局设置为透明的时候需要注意不要在xml布局里面进行设置,在xml布局里面进行设置的话会连同textview一起变为透明;
2、获取图片和标题栏的高度
//获取图片的高度
iv.post(new Runnable() {
@Override
public void run() {
imageHeight=iv.getMeasuredHeight();
}
});
//获取标题栏高度
titleBar.post(new Runnable() {
@Override
public void run() {
measuredHeight = titleBar.getMeasuredHeight();
}
});
在获取一个控件高度或者宽度的时候,直接通过titleBar.getMeasuredHeight();是获取不到的,因为这个时候该控件还没有测量绘制完成,获取到的宽度或者高度为0,可以通过上面的方式获取;
3、监听ScrollView滑动,计算alpha并设置透明度
在调用setOnScrollChangeListener的时候发现,该方法在api23才可以使用,对于api低于23的就不能调用这个方法,不过可以自定义一个ScrollView,重写onScrollChanged方法,在onScrollChanged方法里面进行监听并回调结果;
public class MyScrollView extends ScrollView{
private ScrollChangedListener mListener;
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 监听ScrollView的滑动
* @param l
* @param t
* @param oldl
* @param oldt
*/
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if(mListener!=null){
//将ScrollView滑动监听进行回调
mListener.onScrollChangedListener(l, t, oldl, oldt);
}
}
public interface ScrollChangedListener{
void onScrollChangedListener(int l, int t, int oldl, int oldt);
}
/**
* 设置回调监听
* @param listener
*/
public void setOnScrollChangedListener(ScrollChangedListener listener){
this.mListener=listener;
}
}
这样子就解决了低版本的滑动监听问题,在回调里面根据滑动的距离计算出alpha值并进行设置,效果就实现了;
//监听scrollView滑动
scrollView.setOnScrollChangedListener(new MyScrollView.ScrollChangedListener() {
@Override
public void onScrollChangedListener(int l, int t, int oldl, int oldt) {
// 获取图片的高度,根据当前滚动的位置,计算alpha 值
if (imageHeight == 0) return;
// mImageViewHeight - TitleBar的高度
float alpha = (float) t / (imageHeight-measuredHeight);
if (alpha <= 0) {
alpha = 0;
}
if (alpha > 1) {
alpha = 1;
}
titleBar.getBackground().setAlpha((int) (alpha * 255));
}
});
仿知乎列表滑动隐藏和显示效果
实现仿知乎列表滑动隐藏和显示效果有两种方法,一种为:监听滑动+属性动画;另外一种为:behavior;
方法一:监听滑动+属性动画
主要是监听RecyclerView的滑动,
//滑动监听
recyclerview.addOnScrollListener(new FabScrollListener(this));
在FabScrollListener里面重写onScrolled方法,根据移动的距离进行判断和回调;
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
/**
* dy:Y轴方向的增量
* 有正和负
* 当正在执行动画的时候,就不要再执行了
*
*/
if (distance > THRESHOLD && visible) {
//隐藏动画
visible = false;
hideListener.onHide();
distance = 0;
} else if (distance < -20 && !visible) {
//显示动画
visible = true;
hideListener.onShow();
distance = 0;
}
if (visible && dy > 0 || (!visible && dy < 0)) {
istance += dy;
}
}
在回调方法onHide和onShow中设置属性动画;
@Override
public void onHide() {
//隐藏动画
toolbar.animate().translationY(-toolbar.getHeight()).setInterpolator(new AccelerateInterpolator(3));
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) fab.getLayoutParams();
fab.animate().translationY(fab.getHeight()+layoutParams.bottomMargin).setInterpolator(new AccelerateInterpolator(3));
}
@Override
public void onShow() {
//显示动画
toolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));
fab.animate().translationY(0).setInterpolator(new DecelerateInterpolator(3));
}
效果如下:
方法二:behavior
采用behavior的方式xml的布局要比第一种方式要复杂些;
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="知乎首页"
app:titleTextColor="#ffffff"
app:layout_scrollFlags="scroll|enterAlways"
android:id="@+id/tool_bar"/>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginBottom="70dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:src="@mipmap/add_icon"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:layout_behavior="com.qqtitlebar.TanslationBehavior"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:layout_alignParentBottom="true"
android:background="@android:color/white"
app:layout_behavior="@string/bottom_sheet_behavior">
<ImageView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:src="@mipmap/message_icon" />
<ImageView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:src="@mipmap/message_icon"
/>
<ImageView
android:layout_width="0dp"
android:layout_height="match_parent"
android:src="@mipmap/message_icon"
android:layout_weight="1"/>
<ImageView
android:layout_width="0dp"
android:layout_height="match_parent"
android:src="@mipmap/message_icon"
android:layout_weight="1" />
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
这里涉及到一些Materia Design的控件:CoordinatorLayout、FloatingActionButton、AppBarLayout、Toolbar、RecyclerView,
CoordinatorLayout这里的协调behavior;
这里给RecyclerView和LinearLayout设置了app:layout_behavior,不过这两个都是系统的behavior,
RecyclerView:
app:layout_behavior="@string/appbar_scrolling_view_behavior"
LinearLayout:
app:layout_behavior="@string/bottom_sheet_behavior"
FloatingActionButton设置的是一个自定义的behavior
app:layout_behavior="com.qqtitlebar.TanslationBehavior"
public class TanslationBehavior extends FloatingActionButton.Behavior{
public TanslationBehavior(Context context, AttributeSet attrs){
super(context, attrs);
}
//关注垂直滚动,二轻向上的时候出来,向下是隐藏
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {
return nestedScrollAxes== ViewCompat.SCROLL_AXIS_VERTICAL;
}
private boolean isOut=false;
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
//而且向上的时候是出来的,向下是隐藏
if(dyConsumed>0){
if(!isOut){
//往上滑动,是隐藏,加一个标志为 已经往下走了
int translationY = ((CoordinatorLayout.LayoutParams) child.getLayoutParams()).bottomMargin + child.getMeasuredHeight();
child.animate().translationY(translationY).setDuration(500).start();
isOut=true;
}
}else{
child.animate().translationY(0).setDuration(500).start();
isOut=false;
}
}
}
在自定义behavior的时候需要注意的就是一定要记得写构造方法,在布局文件中引用后,系统去找引用文件中的构造方法,如果没有就会报错;
采用behavior方式不用给RecyclerView设置addOnScrollListener方法进行监听;不过采用behavior方式涉及到很多Materia Design风格的新控件,需要做一些版本兼容,这里运行的环境是android6.0,未做版本兼容;
android6.0效果: