List sliding fixed top bar effect (2)

Alright, let's move on to the previous one. This is what I borrowed from others.

The idea is to simplify the two fixed columns into one, just use removeView and addView to dynamically switch the fixed column inside and outside when the page slides according to the coordinate point.

Let’s borrow a picture from the big guy (thank you): we used getTop() later, and the picture is a comprehensive introduction to each method to measure the value.


When we get the required sliding height, we need to judge the critical value of the fixed layout (here, the current sliding value is set to t, and the required sliding value is y). For example, when our interface first slides up, the t value is If it is less than the y value, the internal fixed column does not need to be removed at this time, and when we slide back beyond the y value and the t value is smaller than the y value, the internal fixed column needs to be removed from the outside and added to the inside. , so here we need to judge the parent layout (ViewGroup) where the fixed bar is located.

Note: Two problems, 1. Don't get the height from the screen in the oncreat() method, you can get the fragment in onResume(), 2. The nesting problem of ScroView and ListView, there are solutions below.

First look at the renderings:


Code directly, customize 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. The current sliding x-axis distance
      * @param t Current vertical scroll origin. The current sliding y-axis distance
      * @param oldl Previous horizontal scroll origin. The last sliding x-axis distance
      * @param oldt Previous vertical scroll origin. The y-axis distance of the previous scroll
      */
     @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);
        }
    }
}

Main interface xml layout

Be sure to remember to add in the parent layout of ScroView

android:focusable="true"

android:focusableInTouchMode="true" otherwise it will not display the first line when it comes in

<?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);

    }

}


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325770084&siteId=291194637