ScrollView+TabLayout+ViewPager+ListView複雜滑動巢狀、上拉載入
阿新 • • 發佈:2019-02-02
先來看看要實現佈局的樣式哈,我感覺手動畫的更詳細嘿嘿。
要實現的就是這樣的複雜佈局,這裡面涉及到各種巢狀滑動的衝突,還涉及ListView的上拉載入,接下來一點點開始哈。
一、ScrollView巢狀ListView
首先從整體看就會看到ScrollView和ListView,我們寫過他倆巢狀的都知道,他倆有衝突,所以這裡我們就要對ListView下手啦。
如果單純的想解決ScrollView巢狀ListView衝突,使用如下方法會出現一個問題,就是ListView會預設的滑到頂部(這個之後說哈)
他倆衝突歸根結底是高度問題,所以解決ListView的高度也就解決他倆的巢狀滑動問題了。
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super (context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_MOVE){
return true;
}
return super.dispatchTouchEvent(ev);
}
}
這裡要注意的是高度給了它最大值,這樣就導致了ListView的滑動監聽失效了,那麼怎麼才能監聽ListView到底了呢?----重寫ScrollView
public class ScrollBottomView extends ScrollView {
private int downX;
private int downY;
private int mTouchSlop;
private int type;
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
private ScrollViewToBottomListener scrollViewListener = null;
private OnScrollViewToBottomLiatener onScrollViewToBottomLiatener = null;
public ScrollBottomView(Context context) {
super(context);
}
public ScrollBottomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ScrollBottomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScrollViewListener(ScrollViewToBottomListener scrollViewListener) {
this.scrollViewListener = scrollViewListener;
}
public void setOnScrollViewToBottomLiatener(OnScrollViewToBottomLiatener onScrollViewToBottomLiatener){
this.onScrollViewToBottomLiatener = onScrollViewToBottomLiatener;
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
View view = (View) getChildAt(getChildCount() - 1);
int d = view.getBottom();
d -= (getHeight() + getScrollY());
// Log.e("---------->","d"+d);
if (d == 0) {
if (onScrollViewToBottomLiatener != null) {
onScrollViewToBottomLiatener.onScrollViewToBottomListener(type);
}
} else {
if (scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, l, t, oldl, oldt);
}
}
}
interface OnScrollViewToBottomLiatener {
void onScrollViewToBottomListener();
}
}
監聽ScrollView是否到底了,間接的監聽了ListView到底了
二、ScrollView巢狀ViewPager
當我們用ScrollView巢狀ViewPager的時候,如果每個ViewPager的頁面高度不同會導致下面有一部分的空白,所以要在ViewPager改變頁面的時候重置它的高度,所以要重寫ViewPager了
public class ChildViewPager extends ViewPager {
private int current;
/**
* 儲存position與對於的View
*/
private HashMap<Integer, Integer> maps = new LinkedHashMap<Integer, Integer>();
public ChildViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChildViewPager(Context context) {
super(context);
}
public int getCurrent() {
return current;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = 0;
// 下面遍歷所有child的高度
for (int i = 0; i < this.getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec,
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
// 採用最大的view的高度
maps.put(i, h);
}
if (getChildCount() > 0) {
height = getChildAt(current).getMeasuredHeight();
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void resetHeight(int current) {
this.current = current;
if (maps.size() > current) {
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
if (layoutParams == null) {
layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, maps.get(current));
} else {
layoutParams.height = maps.get(current);
}
setLayoutParams(layoutParams);
}
}
}
看到resetHeight這個方法沒有,給ViewPager設定監聽,在onPageSelected方法中呼叫這個方法,來改變ViewPager的高度
注意:
1、得到ViewPager後要給它設定一個屬性,來避免ListView滑到頂部
viewPager.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
2、如果編譯時ViewPager出錯要設定如下程式碼
//有幾頁設定幾頁
viewPager.setOffscreenPageLimit(3);
三、ViewPager中的Fragment
public class MyListViewFragment extends Fragment {
private MyListView myListView;
private ArrayAdapter<String> adapter;
private List<String> list;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_mylistview,container,false);
myListView = (MyListView) view.findViewById(R.id.listView);
list = new ArrayList<>();
for(int i =0;i<30;i++){
list.add("資料:"+i);
}
adapter = new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item_1,list);
myListView.setAdapter(adapter);
return view;
}
public void loadData(){
//載入資料list.add()……別忘了重新整理哈
}
}
四、整體的程式碼呼叫
我認為主要的都寫在標註裡了,一個是上拉載入,一個是ViewPager的相關設定
public class ScrollViewEnterActivity extends AppCompatActivity {
private ScrollBottomView mScrollView;
private ChildViewPager viewPager;
private TabLayout tabLayout;
private MyFragmentAdapter adapter;
private List<MyListViewFragment> fragments;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scroll_view_enter);
viewPager = (ChildViewPager) findViewById(R.id.viewpager);
tabLayout = (TabLayout) findViewById(R.id.tablayout);
mScrollView = (ScrollBottomView) findViewById(R.id.activity_scroll_view_enter);
mScrollView.setOnScrollViewToBottomLiatener(new ScrollBottomView.OnScrollViewToBottomLiatener() {
@Override
public void onScrollViewToBottomListener(int type) {
//滑到底部重新整理每個tab中的資料
fragments.get(viewPager.getCurrent()).loadData();
}
});
fragments = new ArrayList<>();
for(int i = 0;i<3;i++){
fragments.add(new MyListViewFragment());
}
adapter = new MyFragmentAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
//設定欲快取頁
viewPager.setOffscreenPageLimit(3);
//我通過xml設定該屬性不管用,這樣好用(不知道為什麼),設定該屬性目的是防止listView(ViewPager)跳到頂部
viewPager.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
viewPager.resetHeight(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
private class MyFragmentAdapter extends FragmentPagerAdapter{
public MyFragmentAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return 3;
}
@Override
public CharSequence getPageTitle(int position) {
return "標題"+position;
}
}
}
看看XML檔案吧
<?xml version="1.0" encoding="utf-8"?>
<com.example.ws.scrollviewdemo.ScrollBottomView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_scroll_view_enter"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.ws.scrollviewdemo.ScrollViewEnterActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="@color/color_00ff66"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="各種View" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="隨意的View" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:background="@color/color_ff6b00" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="動不動" />
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.design.widget.TabLayout>
<com.example.ws.scrollviewdemo.ChildViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</com.example.ws.scrollviewdemo.ChildViewPager>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="1" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="2" />
</LinearLayout>
</com.example.ws.scrollviewdemo.ScrollBottomView>