Listview多tab上滑悬浮

本文介绍了一种实现安卓应用中悬浮头部Tab与下拉刷新功能的方法。通过使用Viewpager与自定义Fragment的方式,实现了多Tab之间的平滑切换及列表的下拉刷新功能。文章详细介绍了整体布局设计、关键代码解析及实现过程中遇到的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这连续两篇其实都已经写了很久了。只是今天在发布到这边

源码地址

       强势插入:不想看文章的同学可以直接去下载源代码,代码地址如下:https://github.com/FreeSunny/RefreashTabView

       在前一期中,我们做了悬浮头部的两个tab切换和下拉刷新效果,后来项目中要求改成三个tab,当时就能估量了一下,如果从之前的改,也不是不可以,但是要互相记住的状态就太多了,很容易出现错误。就决定重新实现一下这个效果,为此先写了一个demo,这期间项目都已经又更新了两个版本了。demo还木有变成文章。

       之前的版本中是采用了一个可以下拉刷新的listview,之后在listview中添加了两个头部,并且在该布局上的上面用了一个一模一样的切换tab,如果没有看过前面版本的,可以看看前一个版本,Listview多Tab上滑悬浮。

实现方式

       基于上述思路我们先来看看页面布局:main_activity

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/color_gray_eaeaea" >

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <LinearLayout
        android:id="@+id/header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:orientation="vertical" >

        <ImageView
            android:id="@+id/show_event_detail_bg"
            android:layout_width="fill_parent"
            android:layout_height="135dip"
            android:contentDescription="@string/empty"
            android:scaleType="fitXY"
            android:src="@drawable/header_default_bk" />

        <TextView
            android:id="@+id/show_event_detail_desc"
            android:layout_width="wrap_content"
            android:layout_height="104dip"
            android:paddingBottom="24dip"
            android:layout_marginLeft="15dip"
            android:layout_marginRight="15dip"
            android:paddingTop="25dip"
            android:text="@string/head_title_desc"
            android:textColor="@color/color_black_333333"
            android:textSize="14sp" />

        <View style="@style/horizontal_gray_divider" />

        <View style="@style/horizontal_gray_divider" />

        <com.example.refreashtabview.sliding.PagerSlidingTabStrip
            android:id="@+id/show_tabs"
            android:layout_width="match_parent"
            android:layout_height="44dip"
            android:background="@color/white" />
    </LinearLayout>

</RelativeLayout>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

       页面采用了两层,后面一层为Viewpager,前面为悬浮头与tab切换,在这大家应该都想到了会怎么样实现,Viewpager中添加已经fragment,每个fragment里面加入一个可下拉刷新的Listview,根据ListView的滑动来控制前一帧页面的位置。

       来看看页面代码吧,MainAcitivity.Java

public class MainActivity extends ActionBarActivity implements OnPageChangeListener, ScrollTabHolder {

    private PagerSlidingTabStrip tabs;

    private ViewPager viewPager;

    private SlidingPagerAdapter adapter;

    private LinearLayout header;

    private int headerHeight;
    private int headerTranslationDis;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        getHeaderHeight();
        findViews();
        setupPager();
        setupTabs();
    }

    private void findViews() {
        tabs = (PagerSlidingTabStrip) findViewById(R.id.show_tabs);
        viewPager = (ViewPager) findViewById(R.id.pager);
        header = (LinearLayout) findViewById(R.id.header);
    }

    private void getHeaderHeight() {
        headerHeight = getResources().getDimensionPixelSize(R.dimen.max_header_height);
        headerTranslationDis = -getResources().getDimensionPixelSize(R.dimen.header_offset_dis);
    }

    private void setupPager() {
        adapter = new SlidingPagerAdapter(getSupportFragmentManager(), this, viewPager);
        adapter.setTabHolderScrollingListener(this);//控制页面上滑
        viewPager.setOffscreenPageLimit(adapter.getCacheCount());
        viewPager.setAdapter(adapter);
        viewPager.setOnPageChangeListener(this);
    }

    private void setupTabs() {
        tabs.setShouldExpand(true);
        tabs.setIndicatorColorResource(R.color.color_purple_bd6aff);
        tabs.setUnderlineColorResource(R.color.color_purple_bd6aff);
        tabs.setCheckedTextColorResource(R.color.color_purple_bd6aff);
        tabs.setViewPager(viewPager);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        tabs.onPageScrolled(position, positionOffset, positionOffsetPixels);
    }

    @Override
    public void onPageSelected(int position) {
        tabs.onPageSelected(position);
        reLocation = true;
        SparseArrayCompat<ScrollTabHolder> scrollTabHolders = adapter.getScrollTabHolders();
        ScrollTabHolder currentHolder = scrollTabHolders.valueAt(position);
        if (NEED_RELAYOUT) {
            currentHolder.adjustScroll((int) (header.getHeight() + headerTop));// 修正滚出去的偏移量
        } else {
            currentHolder.adjustScroll((int) (header.getHeight() + ViewHelper.getTranslationY(header)));// 修正滚出去的偏移量
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        tabs.onPageScrollStateChanged(state);
    }

    @Override
    public void adjustScroll(int scrollHeight) {

    }

    private boolean reLocation = false;

    private int headerScrollSize = 0;

    public static final boolean NEED_RELAYOUT = Integer.valueOf(Build.VERSION.SDK).intValue() < Build.VERSION_CODES.HONEYCOMB;

    private int headerTop = 0;

    // 刷新头部显示时,没有onScroll回调,只有当刷新时会有
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount,
            int pagePosition) {
        if (viewPager.getCurrentItem() != pagePosition) {
            return;
        }
        if (headerScrollSize == 0 && reLocation) {
            reLocation = false;
            return;
        }
        reLocation = false;
        int scrollY = Math.max(-getScrollY(view), headerTranslationDis);
        if (NEED_RELAYOUT) {
            headerTop = scrollY;
            header.post(new Runnable() {

                @Override
                public void run() {
                    Log.e("Main", "scorry1="+ headerTop);
                    header.layout(0, headerTop, header.getWidth(), headerTop + header.getHeight());
                }
            });
        } else {
            ViewHelper.setTranslationY(header, scrollY);
        }
    }

    /**
     * 主要算这玩意,PullToRefreshListView插入了一个刷新头部,因此要根据不同的情况计算当前的偏移量</br>
     * 
     * 当刷新时: 刷新头部显示,因此偏移量要加上刷新头的数值 未刷新时: 偏移量不计算头部。
     * 
     * firstVisiblePosition >1时,listview中的项开始显示,姑且认为每一项等高来计算偏移量(其实只要显示一个项,向上偏移
     * 量已经大于头部的最大偏移量,因此不准确也没有关系)
     * 
     * @param view
     * @return
     */
    public int getScrollY(AbsListView view) {
        View c = view.getChildAt(0);
        if (c == null) {
            return 0;
        }
        int top = c.getTop();
        int firstVisiblePosition = view.getFirstVisiblePosition();
        if (firstVisiblePosition == 0) {
            return -top + headerScrollSize;
        } else if (firstVisiblePosition == 1) {
            return -top;
        } else {
            return -top + (firstVisiblePosition - 2) * c.getHeight() + headerHeight;
        }
    }

    // 与onHeadScroll互斥,不能同时执行
    @Override
    public void onHeaderScroll(boolean isRefreashing, int value, int pagePosition) {
        if (viewPager.getCurrentItem() != pagePosition) {
            return;
        }
        headerScrollSize = value;
        if (NEED_RELAYOUT) {
            header.post(new Runnable() {

                @Override
                public void run() {
                    Log.e("Main", "scorry="+ (-headerScrollSize));
                    header.layout(0, -headerScrollSize, header.getWidth(), -headerScrollSize + header.getHeight());
                }
            });
        }else{
            ViewHelper.setTranslationY(header, -value);
        }
    }

}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163

       解释一下上面的代码,界面中后一层为Viewpager,里面加入了多个fragment,每个fragment里面占用一个Listivew,Listview中添加一个与悬浮头高度完全一样的header,这样可显示区域就为能看到的区域,之后监听Listview的onScorll,根据当前显示的listview的item的高度来控制前一层的悬浮的位置,这个地方要注意的时,每次切换tab时,要将当前已经偏移的位置通知到当前切换的tab,比如tab1,向上滑动,影藏了悬浮头,当从tab1切换到tab2时,这是tab2的位置要向上修正,修正距离为悬浮头滑出去的距离。其他的部分代码页比较简单,看看就可以了,其次开源控件PullToRefreshListView中我修改了当在刷新时偏移的距离,当改距离通知到界面,这样在下拉刷新时,将整个头部向下偏移,

       ps:上面的代码中也看到了,我们针对了不同的版本采用了不同的动画,这是由于在3.0以前,位移动画看起来移动了位置,可是实际上控件还在初始位置,为此要针对不同的版本处理不同的动画,否则tab上的点击事件在2.X版本上还是在初始位置。上面的动画采用了nineold控件,也可以自己写,这个部分动画还是比较简单的。

       上面的Viewpager中添加了Fragment,我们来看看Tab1ListFragment.java

public class Tab1ListFragment extends ScrollTabHolderFragment {

    private PullToRefreshListView listView;

    private View placeHolderView;

    private ArrayAdapter<String> adapter;

    private ArrayList<String> listItems;

    private Handler handler;

    public Tab1ListFragment() {
        this.setFragmentId(PageAdapterTab.PAGE_TAB1.fragmentId);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.page_tab_fragment_layout, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        findViews();
        initListView();
    }

    @SuppressLint("InflateParams")
    private void findViews() {
        handler = new Handler(Looper.getMainLooper());
        listView = (PullToRefreshListView) getView().findViewById(R.id.page_tab_listview);
    }

    private void initListView() {
        setListViewListener();
        listViewAddHeader();
        listViewLoadData();
    }

    private void setListViewListener() {
        listView.setOnRefreshListener(new OnRefreshListener2<ListView>() {

            @Override
            public void onPullDownToRefresh(PullToRefreshBase<ListView> refreshView) {
                loadNews();
            }

            @Override
            public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) {
                loadOlds();
            }

        });

        listView.setOnScrollListener(new OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                if (scrollTabHolder != null) {
                    scrollTabHolder.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount, getFragmentId());
                }
            }
        });
        listView.setOnHeaderScrollListener(new OnHeaderScrollListener() {

            @Override
            public void onHeaderScroll(boolean isRefreashing, boolean istop, int value) {
                if (scrollTabHolder != null && istop) {
                    scrollTabHolder.onHeaderScroll(isRefreashing, value, getFragmentId());
                }
            }
        });
    }

    private void listViewAddHeader() {
        placeHolderView = new LinearLayout(getActivity());
        AbsListView.LayoutParams params = new LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, getResources()
                .getDimensionPixelSize(R.dimen.max_header_height));
        placeHolderView.setLayoutParams(params);
        listView.getRefreshableView().addHeaderView(placeHolderView);
    }

    protected void listViewLoadData() {
        listItems = new ArrayList<String>();
        for (int i = 1; i <= 50; i++) {
            listItems.add("currnet page: " + (getFragmentId() + 1) + " item --" + i);
        }
        adapter = new ArrayAdapter<String>(getActivity(), R.layout.list_item, android.R.id.text1, listItems);
        listView.setAdapter(adapter);
        loadNews();
    }

    /**
     * 下拉清空旧的数据
     */
    private void loadNews() {
        handler.postDelayed(new Runnable() {// 模拟远程获取数据

                    @Override
                    public void run() {
                        stopRefresh();
                        // listItems.clear();
                        // for (int i = 1; i <= 50; i++) {
                        // listItems.add("currnet page: " + (getFragmentId() +
                        // 1) + " item --" + i);
                        // }
                        // notifyAdpterdataChanged();
                    }
                }, 300);
    }

    private void notifyAdpterdataChanged() {
        if (adapter != null) {
            adapter.notifyDataSetChanged();
        }
    }

    protected void loadOlds() {
        handler.postDelayed(new Runnable() {// 模拟远程获取数据

                    @Override
                    public void run() {
                        stopRefresh();
                        int size = listItems.size() + 1;
                        for (int i = size; i < size + 50; ++i) {
                            listItems.add("currnet page: " + (getFragmentId() + 1) + " item --" + i);
                        }
                        notifyAdpterdataChanged();
                    }
                }, 300);
    }

    // PullToRefreshListView 自动添加了一个头部
    @Override
    public void adjustScroll(int scrollHeight) {
        if (scrollHeight == 0 && listView.getRefreshableView().getFirstVisiblePosition() >= 2) {
            return;
        }
        //Log.d(getTag(), "scrollHeight:" + scrollHeight);
        listView.getRefreshableView().setSelectionFromTop(2, scrollHeight);
//      Log.d(getTag(), "getScrollY:" + getScrollY(listView.getRefreshableView()));
//      handler.postDelayed(new Runnable() {
//          
//          @Override
//          public void run() {
//              Log.d(getTag(), "getScrollY:" + getScrollY(listView.getRefreshableView()));             
//          }
//      }, 5000);
    }

    public int getScrollY(AbsListView view) {
        View c = view.getChildAt(0);
        if (c == null) {
            return 0;
        }
        int top = c.getTop();
        int firstVisiblePosition = view.getFirstVisiblePosition();
        if (firstVisiblePosition == 0) {
            return -top;
        } else if (firstVisiblePosition == 1) {
            return top;
        } else {
            return -top + (firstVisiblePosition - 2) * c.getHeight() + 683;
        }
    }

    protected void updateListView() {
        if (adapter != null) {
            adapter.notifyDataSetChanged();
        }
    }

    protected void stopRefresh() {
        listView.onRefreshComplete();
    }

}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187

       上面代码中的界面就是xml中包含了一个PullToRefreshListView,比较简单这个地方就不贴出来了,我们看到在listViewAddHeader中,这个地方添加了一个与悬浮头等高的头部,这样就可以将内容区域给呈现出来,不会被悬浮头遮挡,其次在list的listener中我们将onScorll传到了主界面,这样Listview滚动,就可以将当前滚动的距离计算出来,修正悬浮头的距离。

       我们再贴出上面剩下的代码ScrollTabHolderFragment.java与ScrollTabHolder.java

public abstract class ScrollTabHolderFragment extends Fragment implements ScrollTabHolder {

    private int fragmentId;

    protected ScrollTabHolder scrollTabHolder;

    public void setScrollTabHolder(ScrollTabHolder scrollTabHolder) {
        this.scrollTabHolder = scrollTabHolder;
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount,
            int pagePosition) {
        // nothing
    }

    @Override
    public void onHeaderScroll(boolean isRefreashing, int value, int pagePosition) {

    }

    public int getFragmentId() {
        return fragmentId;
    }

    public void setFragmentId(int fragmentId) {
        this.fragmentId = fragmentId;
    }
}


public interface ScrollTabHolder {

    void adjustScroll(int scrollHeight);

    void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount, int pagePosition);

    void onHeaderScroll(boolean isRefreashing, int value, int pagePosition);
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

       最后我们来看看adaper

public class SlidingPagerAdapter extends FragmentPagerAdapter {

    protected final ScrollTabHolderFragment[] fragments;

    protected final Context context;

    private SparseArrayCompat<ScrollTabHolder> mScrollTabHolders;
    private ScrollTabHolder mListener;

    public int getCacheCount() {
        return PageAdapterTab.values().length;
    }

    public SlidingPagerAdapter(FragmentManager fm, Context context, ViewPager pager) {
        super(fm);
        fragments = new ScrollTabHolderFragment[PageAdapterTab.values().length];
        this.context = context;
        mScrollTabHolders = new SparseArrayCompat<ScrollTabHolder>();
        init(fm);
    }

    private void init(FragmentManager fm) {
        for (PageAdapterTab tab : PageAdapterTab.values()) {
            try {
                ScrollTabHolderFragment fragment = null;

                List<Fragment> fs = fm.getFragments();
                if (fs != null) {
                    for (Fragment f : fs) {
                        if (f.getClass() == tab.clazz) {
                            fragment = (ScrollTabHolderFragment) f;
                            break;
                        }
                    }
                }

                if (fragment == null) {
                    fragment = (ScrollTabHolderFragment) tab.clazz.newInstance();
                }

                fragments[tab.tabIndex] = fragment;
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    public void setTabHolderScrollingListener(ScrollTabHolder listener) {
        mListener = listener;
    }

    @Override
    public ScrollTabHolderFragment getItem(int pos) {
        ScrollTabHolderFragment fragment = fragments[pos];
        mScrollTabHolders.put(pos, fragment);
        if (mListener != null) {
            fragment.setScrollTabHolder(mListener);
        }
        return fragment;
    }

    public SparseArrayCompat<ScrollTabHolder> getScrollTabHolders() {
        return mScrollTabHolders;
    }

    @Override
    public int getCount() {
        return PageAdapterTab.values().length;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        PageAdapterTab tab = PageAdapterTab.fromTabIndex(position);
        int resId = tab != null ? tab.resId : 0;
        return resId != 0 ? context.getText(resId) : "";
    }

}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

       SlidingPagerAdapter 继承自FragmentPagerAdapter,从主界面传递了一个callback,将在callback传递给每个fragment,这样就将fragment与activity联系起来了。其实还有很多种方式,比如在fragment的attach中获取activity中的回调。上面代码中还有一个PageAdapterTab,它又是干什么的呐?来看看代码

public enum PageAdapterTab {
    PAGE_TAB1(0, Tab1ListFragment.class, R.string.page_tab1),

    PAGE_TAB2(1, Tab2ListFragment.class, R.string.page_tab2),

    PAGE_TAB3(2, Tab3ListFragment.class, R.string.page_tab3),
    ;

    public final int tabIndex;

    public final Class<? extends Fragment> clazz;

    public final int resId;

    public final int fragmentId;

    private PageAdapterTab(int index, Class<? extends Fragment> clazz, int resId) {
        this.tabIndex = index;
        this.clazz = clazz;
        this.resId = resId;
        this.fragmentId = index;
    }

    public static final PageAdapterTab fromTabIndex(int tabIndex) {
        for (PageAdapterTab value : PageAdapterTab.values()) {
            if (value.tabIndex == tabIndex) {
                return value;
            }
        }

        return null;
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

       就是一个枚举类,配置了当前要显示的fragment,这样以后就要增加就可以只修改改枚举就ok了

实现效果

到此整个工程就结束了,我们截几张图看看效果,其实是可以截图gif图来做效果演示的,这里先就截取静态图作为演示,后续不上gif图: 
这里写图片描述这里写图片描述这里写图片描述

总结

       最后在回顾一下,布局为两层,底部一层为一个Viewpager,里面包含了多个fragment,前一层为一个悬浮头与切换tab,当滑动listview时将当前显示的位置传递到主界面,同时更改主界面悬浮头的位置。

后续任务

       ps:有同学反应说如果悬浮头如果是一个TextView占满整个空间,且有点击事件,即TextView设置textView.setMovementMethod(LinkMovementMethod.getInstance());这时滑动时,会textview接受到该事件,并进行处理。导致不能滑动,因此后续还需要对该效果进行调整,重写整个事件,并且将事件位置传递到后面的ListView。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值