如上图美团网,大众点评购买框悬浮效果,用户在向上滑动界面时,购买按钮始终在界面上,用户体验很好,很人性化。下面具体看看代码是怎么实现的:
主界面布局文件main.xml
<?xml version="1.0" encoding="UTF-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/parent_layout" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ImageView android:id="@+id/imageView1" android:layout_width="match_parent" android:layout_height="45dip" android:scaleType="centerCrop" android:src="@drawable/navigation_bar" /> <com.example.meituandemo.MyScrollView android:id="@+id/scrollView" android:layout_width="fill_parent" android:layout_height="fill_parent" > <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <ImageView android:id="@+id/iamge" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/pic" android:scaleType="centerCrop" /> <include android:id="@+id/buy" layout="@layout/buy_layout" /> <ImageView android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/one" android:scaleType="centerCrop" /> <ImageView android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/one" android:scaleType="centerCrop" /> <ImageView android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/one" android:scaleType="centerCrop" /> </LinearLayout> <include android:id="@+id/top_buy_layout" layout="@layout/buy_layout" /> </FrameLayout> </com.example.meituandemo.MyScrollView> </LinearLayout>
立即抢购悬浮框布局文件buy_layout.xml
<?xml version="1.0" encoding="UTF-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" > <ImageView android:id="@+id/buy_layout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/buy" /> </LinearLayout>
自定义ScrollView控件MyScrollView类
package com.greentree.meituanxuanfudemo; import android.content.Context; import android.util.AttributeSet; import android.widget.ScrollView; public class MyScrollView extends ScrollView { private OnScrollListener onScrollListener; public MyScrollView(Context context) { super(context); } public MyScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public MyScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } /** * 设置滚动接口 * * @param onScrollListener */ public void setOnScrollListener(OnScrollListener onScrollListener) { this.onScrollListener = onScrollListener; } @Override public int computeVerticalScrollRange() { return super.computeVerticalScrollRange(); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (onScrollListener != null) { onScrollListener.onScroll(t); } } /** * * 滚动的回调接口 * * @author xiaanming * */ public interface OnScrollListener { /** * 回调方法, 返回MyScrollView滑动的Y方向距离 * * @param scrollY */ public void onScroll(int scrollY); } }
主界面MainActivity
package com.greentree.meituanxuanfudemo; import com.greentree.meituanxuanfudemo.MyScrollView.OnScrollListener; import android.app.Activity; import android.os.Bundle; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.LinearLayout; public class MainActivity extends Activity implements OnScrollListener { /** * 自定义的MyScrollView */ private MyScrollView myScrollView; /** * 在MyScrollView里面的购买布局 */ private LinearLayout mBuyLayout; /** * 位于顶部的购买布局 */ private LinearLayout mTopBuyLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myScrollView = (MyScrollView) findViewById(R.id.scrollView); mBuyLayout = (LinearLayout) findViewById(R.id.buy); mTopBuyLayout = (LinearLayout) findViewById(R.id.top_buy_layout); myScrollView.setOnScrollListener(this); // 当布局的状态或者控件的可见性发生改变回调的接口 findViewById(R.id.parent_layout).getViewTreeObserver() .addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // 这一步很重要,使得上面的购买布局和下面的购买布局重合 onScroll(myScrollView.getScrollY()); } }); } @Override public void onScroll(int scrollY) { int mBuyLayout2ParentTop = Math.max(scrollY, mBuyLayout.getTop()); mTopBuyLayout.layout(0, mBuyLayout2ParentTop, mTopBuyLayout.getWidth(), mBuyLayout2ParentTop + mTopBuyLayout.getHeight()); } }
主界面就短短的几行代码,可能看完这些代码你还是没有明白到底是怎么做到的,没关系,我给大家说说,其实我们是让上面的购买布局和下面的购买布局重合起来了,layout()这个方法是确定View的大小和位置的,然后将其绘制出来,里面的四个参数分别是View的四个点的坐标,他的坐标不是相对屏幕的原点,而且相对于他的父布局来说的,
我们在主页面最外层的ViewGroup添加了布局状态改变的监听器,当绘制完了屏幕会回调到方法onGlobalLayout()中,我们在onGlobalLayout()方法中手动调用了下onScroll()方法,刚开始myScrollView.getScrollY()等于0,所以说当scrollY小于mBuyLayout.getTop()的时候,上面的购买布局的上边缘到myScrollView的上边缘的距离等于mBuyLayout.getTop()(即下面布局的上边缘到myScrollView的上边缘)所以刚开始上面的购买布局和下面的购买布局重合了。
当myScrollView向上滚动,而上面购买布局的上边缘始终要和myScrollView的上边缘保持mBuyLayout.getTop()这个距离,所以上面的购买布局也跟着向上滚动,当scrollY大于mBuyLayout.getTop()的时候,表示购买布局上边缘滑动到了导航栏布局,所以此时购买布局的上边缘与myScrollView的上边缘始终要保持scrollY这个距离,所以购买布局才会一直在导航栏下面,就好像粘住了一样,不知道你了解了没有?好了,不过根据这种思路你也可以刚开始使用一个悬浮框来覆盖在下面的购买布局上面,然后onScroll()方法中更新悬浮框的位置,不过悬浮框的x,y不是相对于父布局的,这点要注意下,这样子也能实现效果,不过相对于此,要复杂的多,所以我们遇到类似的功能直接使用这种就行了,简洁明了,好了,你是不迫不及待的想看下效果,那我们接下来就运行下程序吧
demo是根据原博主博文写的,图片都是截图的,所以界面会比较难看,请大家不要注意这些细节~!