借鉴洪洋大神StickyNavLayout的demo加入自己的注解,方便以后查阅。
一.思绪整理:
1.要实现控件经包含的可滑动的view向上滑去使得头部隐藏,下面包含的view固定。拿到需求的第一想法是用scrollview去做,要这样的话可能要解决滑动的冲突的问题。呵呵这个常规的想法也没错就是实现有点麻烦。
2.网上百度了一下很多的大神已经实现的这种的demo,如洪洋大神的StickyNavLayout和玉刚大神的StickLayout都是很高的借鉴例子。
3.看了代码思路是:自定义view继承LinearLayout,获取子view的高度最重要是获取要隐藏topview,之后用在写滑动的逻辑代码。
图片效果:
1.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.foreveross.myapplication.view.StickyNavLayout
android:id="@+id/stickylayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="头部"
android:textSize="40dp"/>
<TextView
android:id="@+id/ll_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="固定部分"
android:textSize="40dp"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/activity_recycleview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#ffff0000"
android:dividerHeight="10dp">
</android.support.v7.widget.RecyclerView>
</com.example.foreveross.myapplication.view.StickyNavLayout>
</LinearLayout>
package com.example.foreveross.myapplication.view;
import android.content.Context;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.OverScroller;
import com.example.foreveross.myapplication.R;
public class StickyNavLayout extends LinearLayout implements NestedScrollingParent
{
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes)
{
return true;
}
@Override
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes)
{
}
@Override
public void onStopNestedScroll(View target)
{
}
@Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)
{
}
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed)
{
//进行头部view是否隐藏的判断
boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight;
boolean showTop = dy < 0 && getScrollY() >= 0 && !ViewCompat.canScrollVertically(target, -1);
if (hiddenTop || showTop)
{
scrollBy(0, dy);
consumed[1] = dy;
}
}
@Override
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed)
{
return false;
}
@Override
public boolean onNestedPreFling(View target, float velocityX, float velocityY)
{
//down - //up+
if (getScrollY() >= mTopViewHeight) return false;
fling((int) velocityY);
return true;
}
@Override
public int getNestedScrollAxes()
{
return 0;
}
private View mTop;
private View mNav;
private RecyclerView mRecyclerView;
private int mTopViewHeight; //顶部view滚动的高度
private OverScroller mScroller;
private VelocityTracker mVelocityTracker; //速度
private int mTouchSlop;
private int mMaximumVelocity, mMinimumVelocity;
private float mLastY;
private boolean mDragging;
public StickyNavLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
setOrientation(LinearLayout.VERTICAL);
mScroller = new OverScroller(context);
//getScaledTouchSlop是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。
// 如果小于这个距离就不触发移动控件
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mMaximumVelocity = ViewConfiguration.get(context)
.getScaledMaximumFlingVelocity();
mMinimumVelocity = ViewConfiguration.get(context)
.getScaledMinimumFlingVelocity();
}
private void initVelocityTrackerIfNotExists()
{
if (mVelocityTracker == null)
{
mVelocityTracker = VelocityTracker.obtain();
}
}
private void recycleVelocityTracker()
{
if (mVelocityTracker != null)
{
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
/**
* 当viewgroup填充完成后执行的
*/
@Override
protected void onFinishInflate()
{
super.onFinishInflate();
mTop = findViewById(R.id.tv); //可隐藏的头部
mNav = findViewById(R.id.ll_tv); //滑动上去可固定的
View view = findViewById(R.id.activity_recycleview); //listview
mRecyclerView = (RecyclerView) view;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取第一个子view把测量模式设为UNSPECIFIED
getChildAt(0).measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
//获取RecyclerView的高度并设置高度
ViewGroup.LayoutParams params = mRecyclerView.getLayoutParams();
params.height = getMeasuredHeight() - mNav.getMeasuredHeight();
//最终的测量设置StickyNavLayout的高度
setMeasuredDimension(getMeasuredWidth(), mTop.getMeasuredHeight() + mNav.getMeasuredHeight() + mRecyclerView.getMeasuredHeight());
}
/**
* 当view的大小改变时调用
* @param w
* @param h
* @param oldw
* @param oldh
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
mTopViewHeight = mTop.getMeasuredHeight();
}
public void fling(int velocityY)
{
mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight);
invalidate();
}
/**
* 滚动到位置的设置
* @param x
* @param y
*/
@Override
public void scrollTo(int x, int y)
{
if (y < 0)
{
y = 0;
}
if (y > mTopViewHeight)
{
y = mTopViewHeight;
}
if (y != getScrollY())
{
super.scrollTo(x, y);
}
}
@Override
public void computeScroll()
{
if (mScroller.computeScrollOffset())
{
scrollTo(0, mScroller.getCurrY());
invalidate();
}
}
}
感悟总结:
一个星期不知不觉就过了,生活就是一样白驹过隙一般。当自己全身心的投入到一件事情当中时虽然痛苦和折磨是伴随其中的,思想的反复挣扎,达不到预期的心情低落;但当全然豁然开朗时却把一切阴霾吹得烟消云散。
个人坚持和外部的压力,杂糅成一团乱码。就像BUG一样出现得不知所以然,而只有是越挫越勇之后的静下心来的梳理思绪才能理顺BUG的出处。
一个需求网上大神虽然有类似的demo可借鉴,而自己却急于实现而不去吸收理解。只求快速地实现功能。这是丢了西瓜拣芝麻的事,使得自己精髓没学到却出现一大堆bug,
以后切记着这好高骛远的想法,踏踏实实去消化吸收。