列表滑动固定顶部栏效果(二)
好了,接着上篇的来。这是我从别人那借鉴的。
思路是把两个固定栏简化成了一个,只是利用removeView和addView根据坐标点在页面滑动的时候动态的把固定栏在内外部切换。
先借大佬一张图(谢谢了):我们后面用到了getTop(),图片很全面的介绍了各方法取值得衡量方式
当我们拿到所需要滑动的高度时,我们需要对固定布局进行临界值做判断(这里设当前滑动值为t,所需滑动值为y)比如当我们界面一开始向上滑的时候t值是小于y值的,此时内部固定栏是不需要移除的,而当我们超过y值往回滑t值又小于y值的时候,此时内部固定栏是需要从外部移除添加到内部的,所以这里我们需要对固定栏所在的父布局(ViewGroup)做判断。
注意:两个问题,1.不要在oncreat()方法中获取距离屏幕的高度,fragmen在onResume()中获取即可,2.ScroView与ListView嵌套问题,下面有解决方案。
先看效果图:
直接上代码,自定义ScroView
public class ObservableScrollView extends ScrollView{ public ObservableScrollView(Context context) { this(context,null); } public ObservableScrollView(Context context, AttributeSet attrs) { this(context, attrs,0); } public ObservableScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private OnObservableScrollViewScrollChanged mOnObservableScrollViewScrollChanged; public void setOnObservableScrollViewScrollChanged(OnObservableScrollViewScrollChanged mOnObservableScrollViewScrollChanged) { this.mOnObservableScrollViewScrollChanged = mOnObservableScrollViewScrollChanged; } public interface OnObservableScrollViewScrollChanged{ void onObservableScrollViewScrollChanged(int l, int t, int oldl, int oldt); } /** * @param l Current horizontal scroll origin. 当前滑动的x轴距离 * @param t Current vertical scroll origin. 当前滑动的y轴距离 * @param oldl Previous horizontal scroll origin. 上一次滑动的x轴距离 * @param oldt Previous vertical scroll origin. 上一次滑动的y轴距离 */ @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if(mOnObservableScrollViewScrollChanged!=null){ mOnObservableScrollViewScrollChanged.onObservableScrollViewScrollChanged(l,t,oldl,oldt); } } }
主界面xml布局
一定记着在ScroView的父布局加
android:focusable="true"
android:focusableInTouchMode="true"否则一进来显示的不是第一行
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:focusable="true" android:focusableInTouchMode="true" android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.administrator.scrollviewhead.ObservableScrollView android:id="@+id/sv_contentView" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:id="@+id/ll_contentView" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/tv_headerView" android:layout_width="match_parent" android:layout_height="200dp" android:text="头部布局" android:textSize="30sp" android:background="@color/colorAccent" android:gravity="center"/> <LinearLayout android:id="@+id/ll_topView" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="center" android:orientation="vertical"> <TextView android:id="@+id/tv_topView" android:layout_width="match_parent" android:layout_height="50dp" android:text="内层固定的布局" android:background="@color/colorPrimaryDark" android:textSize="30sp" android:gravity="center"/> </LinearLayout> <ListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentTop="true" android:fadingEdge="none"/> </LinearLayout> </com.example.administrator.scrollviewhead.ObservableScrollView> <LinearLayout android:id="@+id/ll_fixedView" android:layout_width="match_parent" android:layout_height="50dp" android:orientation="vertical"/> </FrameLayout>
再看下主界面代码:
public class HomeActivity extends AppCompatActivity implements ObservableScrollView.OnObservableScrollViewScrollChanged{ private ObservableScrollView sv_contentView; private LinearLayout ll_topView; private TextView tv_topView; private LinearLayout ll_fixedView; private ListView listView; MyAdapter adpater; //用来记录内层固定布局到屏幕顶部的距离 private int mHeight; public HomeActivity() { } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_homes); sv_contentView= (ObservableScrollView) findViewById(R.id.sv_contentView); ll_topView= (LinearLayout) findViewById(R.id.ll_topView); tv_topView= (TextView) findViewById(R.id.tv_topView); ll_fixedView= (LinearLayout) findViewById(R.id.ll_fixedView); listView= (ListView) findViewById(R.id.lv); List<String> data = new ArrayList<>(100); for (int i = 0; i < 100; ++i) { data.add(String.valueOf(i)); } sv_contentView.setOnObservableScrollViewScrollChanged(this); adpater = new MyAdapter(data,this); listView.setAdapter(adpater); // 重新计算ListView的高度 UIUtils.fixListViewHeight(listView); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if(hasFocus){ //获取HeaderView的高度,当滑动大于等于这个高度的时候,需要把topView移除当前布局,放入到外层布局 mHeight=ll_topView.getTop(); } } /** * @param l Current horizontal scroll origin. 当前滑动的x轴距离 * @param t Current vertical scroll origin. 当前滑动的y轴距离 * @param oldl Previous horizontal scroll origin. 上一次滑动的x轴距离 * @param oldt Previous vertical scroll origin. 上一次滑动的y轴距离 */ @Override public void onObservableScrollViewScrollChanged(int l, int t, int oldl, int oldt) { if(t>=mHeight){ if(tv_topView.getParent()!=ll_fixedView){ ll_topView.removeView(tv_topView); ll_fixedView.addView(tv_topView); } }else{ if(tv_topView.getParent()!=ll_topView){ ll_fixedView.removeView(tv_topView); ll_topView.addView(tv_topView); } } } }
Adapter代码:
public class MyAdapter extends BaseAdapter { List<String> data; Context context; public MyAdapter(List<String> data, Context context) { this.data = data; this.context = context; } @Override public int getCount() { return data.size(); } @Override public Object getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = LayoutInflater.from(context).inflate(R.layout.item_taobao_product,null); TextView tvName = view.findViewById(R.id.tv_title); tvName.setText(data.get(position)); return view; } }
但是这里有个问题就是ScroView嵌套ListView会只显示一行的ListView,所以要动态计算每个item的高度,动态设置ListView的高度。下面是计算的工具类
public class UIUtils { public static void fixListViewHeight(ListView listView) { // 如果没有设置数据适配器,则ListView没有子项,返回。 ListAdapter listAdapter = listView.getAdapter(); int totalHeight = 0; if (listAdapter == null) { return; } int len = listAdapter.getCount(); for (int index = 0; index < len; index++) { View listViewItem = listAdapter.getView(index , null, listView); // 计算子项View 的宽高 listViewItem.measure(0, 0); // 计算所有子项的高度和 totalHeight += listViewItem.getMeasuredHeight(); } ViewGroup.LayoutParams params = listView.getLayoutParams(); // listView.getDividerHeight()获取子项间分隔符的高度 // params.height设置ListView完全显示需要的高度 params.height = totalHeight+ (listView.getDividerHeight() * (listAdapter.getCount() - 1)); listView.setLayoutParams(params); } }