ScrollView头部悬停
1. 效果2.分析和实现2.1效果分析: 1.RelativeLayout根布局中放置一个ScrollView,隐藏一个需要悬浮的一模一样的View,对ScrollView进行监听滚动到头部的时候将其显示,其余时候将其隐藏; 2.布局中放置一个固定在头部的容器,当ScrollView滚动到悬浮的View时,将悬浮的View从原来的布局中移除添加到头部容器中,向下滚动的时候将其移回到原来的布局
1. 效果
2.分析和实现
2.1效果分析:
1.RelativeLayout根布局中放置一个ScrollView,隐藏一个需要悬浮的一模一样的View,对ScrollView滚动进行监听,当滚动位置大于悬浮View距顶部的距离时候将其显示,否则将其隐藏;
2.布局中放置一个固定在头部的容器,对ScrollView滚动进行监听,当滚动位置大于悬浮View距顶部的距离时候将悬浮的View从原来的布局中移除并添加到头部容器中,否则将其移回到原来的布局中
无论用哪种方式其实实现起来都挺简单的,原理也都是一样的,第一种可能在性能方面相对要好一点,只不过在每一个地方可能要多写几行代码,这里实现第二种自定义TopFloatScrollView
2.2效果实现:
1.自定义的View继承RelativeLayout在构造函数中添加两个布局,一个是滚动的ScrollView,一个是隐藏在头部存放需要存放悬浮View的容器LinearLayout;
2.在onFinishInflate()中首先判断一下在该自定义View中是否只包含一个孩子,将其取出来放置到ScrollView中,然后我们根据需要悬浮的ViewId找到悬浮的View,之后我们确定悬浮View在父容器中位置,在其上面再包上一层View,以免移除的时候界面会回缩;
3.在onWindowFocusChanged()中获取悬浮View距顶部的距离,同时我们获取悬浮View的bitmap作为头部容器布局的背景,之所以这么搞是因为在快速滑动下界面会出现闪动的情况;
4.监听自定义ScrollView的滚动,如果滚动的位置大于悬浮View到头部的距离将其添加到头部的容器布局中,如果小于了移到原来的布局中。
1.自定义TopFloatScrollView在其构造方法中添加一个ScrollView和隐藏在顶部需要临时存放需要悬浮的容器LinearLayout;
public class TopFloatScrollView extends FrameLayout implements OnScrollListener {
private Context mContext;
// 滚动的内容ScrollView,自定义可以监听滚动
private MonitorScrollView mContainerSv;
// ScrollView 的根内容
private ViewGroup mRootView;
// 一直在头部的容器
private RelativeLayout mFloatTopContainer;
public TopFloatScrollView(Context context) {
this(context, null);
}
public TopFloatScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TopFloatScrollView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
initView();
}
private void initView() {
// 添加滚动的ScrollView
mContainerSv = new MonitorScrollView(mContext);
// 设置滚动监听
mContainerSv.setOnScrollListener(this);
FrameLayout.LayoutParams params = new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mContainerSv.setLayoutParams(params);
super.addView(mContainerSv);
// 添加一直隐藏在头部容器
mFloatTopContainer = new RelativeLayout(mContext);
super.addView(mFloatTopContainer);
mFloatTopContainer.setVisibility(View.GONE);
}
2.在onFinishInflate()中首先判断一下在该自定义View中是否只包含一个孩子,将其取出来放置到ScrollView中,然后我们根据需要悬浮的ViewId找到悬浮的View,之后我们确定悬浮View在父容器中位置,在其上面再包上一层View,以免到时候移除的时候界面会回缩;
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() > 3) {
// 只容许在xml中配置一个ChildView
throw new IllegalStateException(
"TopFloatScrollView can host only one direct child");
}
initInnerView();
}
/**
* 初始化内部的View
*/
private void initInnerView() {
// 判断是不是只有一个ChildView
int childCount = this.getChildCount();
if (childCount == 2) {
// 刚开始可能没有在layout中设置子View,需要在代码中动态添加
return;
}
// 获取ScrollView的内容
mRootView = (ViewGroup) this.getChildAt(2);
if (mRootView == null) {
return;
}
// 先从this里面移除
this.removeView(mRootView);
// 添加到ScrollView里面
mContainerSv.addView(mRootView);
// 获取悬浮的View
mFloatView = mRootView.findViewById(R.id.top_float);
if (mFloatView == null) {
throw new RuntimeException(
"Can't find the float View for id is top_float");
}
// 找到FloatView在这个RootView中的位置
findFloatViewPosition();
// 把悬浮View放到新增容器中然后把新增容器放到主布局中
initAddFloatContainerView();
}
/**
* 把悬浮View放到新增容器中然后把新增容器放到主布局中
*/
private void initAddFloatContainerView() {
if (mFloatView != null) {
GeneralUtil.measureView(mFloatView);
mRootView.removeView(mFloatView);
mAddFloatContainer.addView(mFloatView);
mRootView.addView(mAddFloatContainer, mFloatViewPotion);
// 设置临时高度
mAddFloatContainer.getLayoutParams().height = mFloatView
.getMeasuredHeight();
}
}
/**
* 找到FloatView在这个RootView中的位置
*/
private void findFloatViewPosition() {
int count = mRootView.getChildCount();
for (int position = 0; position < count; position++) {
View childView = mRootView.getChildAt(position);
if (childView == mFloatView) {
mFloatViewPotion = position;
break;
}
}
}
3.在onWindowFocusChanged()中获取悬浮View距顶部的距离,同时我们获取悬浮View的bitmap作为头部容器布局的背景,之所以这么搞是因为在快速滑动下界面会出现闪动的情况;
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus && mAddFloatContainer != null && mFloatView != null) {
// 获取需要悬浮的View到这个顶部的高度
mFloatToTopHeight = mAddFloatContainer.getTop();
mFloatTopContainer.getLayoutParams().height = mFloatView
.getMeasuredHeight();
// 之所以要设置背景是因为快速向上滑动的时候会出现闪现的情况
mFloatTopContainer.setBackground(BitmapUtil
.bitmap2Drawable(BitmapUtil.getBitmapFromView(mFloatView)));
}
}
4.监听自定义ScrollView的滚动,如果滚动的位置大于悬浮View到头部的距离将其添加到头部的容器布局中,如果小于了移到原来的布局中。
// 滚动Y位置监听
@Override
public void onScroll(int y) {
if (mFloatView != null) {
if (y > mFloatToTopHeight) {
addFloatToContainer();
} else {
addFloatToRootView();
}
}
}
/**
* 添加到原来的RooView中
*/
private void addFloatToRootView() {
ViewParent currentParent = mFloatView.getParent();
if (currentParent != mAddFloatContainer) {
// 添加到原来的RooView中 我们之前有记录位置
mFloatTopContainer.removeView(mFloatView);
mAddFloatContainer.addView(mFloatView);
mFloatTopContainer.setVisibility(View.GONE);
}
}
/**
* 添加悬浮到这个准备好的容器中
*/
private void addFloatToContainer() {
ViewParent currentParent = mFloatView.getParent();
if (currentParent != mFloatTopContainer) {
// 添加到容器中
mFloatTopContainer.setVisibility(View.VISIBLE);
mAddFloatContainer.removeAllViews();
mFloatTopContainer.addView(mFloatView);
}
}
/************************重载所有的addView()方法************************/
...
...
使用的时候我们将需要悬浮View的id设置成我们约定的android:id=”@id/top_float”,也和ScrollView 添加布局一样只允许一个子View,否则都会抛运行时的异常
源码下载地址:http://download.csdn.net/detail/z240336124/9440706
更多推荐
所有评论(0)