在前兩篇文章中,分別介紹了tablayout+scrollview 和 tablayout+recyclerview 實現的滑動定位的功能,文章鏈接:
Android 實現錨點定位
Android tabLayout+recyclerView實現錨點定位
仔細看的話,這種滑動定位的功能,還可以整體滑動,再加上頂部tablayout 吸附懸停的效果。
實現效果:
布局
這里采用的是兩個 tablayout。
一個用於占位,位於原始位置,scrollview內部,隨scrollview滾動;另一個則是在滑動過程中,不斷滑動,滑動到頂部時吸附在屏幕頂部,用戶實際操作的也是這個tablayout。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.tabscroll.CustomScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#ccc"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="這里是頂部內容區域"
android:textSize="16sp" />
</LinearLayout>
<!--占位的tablayout-->
<android.support.design.widget.TabLayout
android:id="@+id/tablayout_holder"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#ffffff"
app:tabIndicatorColor="@color/colorPrimary"
app:tabMode="scrollable"
app:tabSelectedTextColor="@color/colorPrimary" />
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp" />
</LinearLayout>
<!--實際用戶操作的tablayout-->
<android.support.design.widget.TabLayout
android:id="@+id/tablayout_real"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#ffffff"
android:visibility="invisible"
app:tabIndicatorColor="@color/colorPrimary"
app:tabMode="scrollable"
app:tabSelectedTextColor="@color/colorPrimary" />
</FrameLayout>
</com.tabscroll.CustomScrollView>
</LinearLayout>
實現
滑動定位的功能可以參考之前的文章,這里主要是進行吸附懸停的效果。
數據初始化:
/**
* 占位tablayout,用於滑動過程中去確定實際的tablayout的位置
*/
private TabLayout holderTabLayout;
/**
* 實際操作的tablayout,
*/
private TabLayout realTabLayout;
private CustomScrollView scrollView;
private LinearLayout container;
private String[] tabTxt = {"客廳", "卧室", "餐廳", "書房", "陽台", "兒童房"};
private List<AnchorView> anchorList = new ArrayList<>();
//判讀是否是scrollview主動引起的滑動,true-是,false-否,由tablayout引起的
private boolean isScroll;
//記錄上一次位置,防止在同一內容塊里滑動 重復定位到tablayout
private int lastPos = 0;
//監聽判斷最后一個模塊的高度,不滿一屏時讓最后一個模塊撐滿屏幕
private ViewTreeObserver.OnGlobalLayoutListener listener;
for (int i = 0; i < tabTxt.length; i++) {
AnchorView anchorView = new AnchorView(this);
anchorView.setAnchorTxt(tabTxt[i]);
anchorView.setContentTxt(tabTxt[i]);
anchorList.add(anchorView);
container.addView(anchorView);
}
for (int i = 0; i < tabTxt.length; i++) {
holderTabLayout.addTab(holderTabLayout.newTab().setText(tabTxt[i]));
realTabLayout.addTab(realTabLayout.newTab().setText(tabTxt[i]));
}
一開始讓實際的tablayout 移動到占位的tablayout 處,覆蓋占位的tablayout。
listener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//計算讓最后一個view高度撐滿屏幕
int screenH = getScreenHeight();
int statusBarH = getStatusBarHeight(AliHomeMoreActivity.this);
int tabH = holderTabLayout.getHeight();
int lastH = screenH - statusBarH - tabH - 16 * 3;
AnchorView anchorView = anchorList.get(anchorList.size() - 1);
if (anchorView.getHeight() < lastH) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.height = lastH;
anchorView.setLayoutParams(params);
}
//一開始讓實際的tablayout 移動到 占位的tablayout處,覆蓋占位的tablayout
realTabLayout.setTranslationY(holderTabLayout.getTop());
realTabLayout.setVisibility(View.VISIBLE);
container.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
};
container.getViewTreeObserver().addOnGlobalLayoutListener(listener);
private int getScreenHeight() {
return getResources().getDisplayMetrics().heightPixels;
}
public int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources()
.getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
scrollview滑動
主要在滑動過程這不斷監聽滑動的距離,再移動實際的tablayout ,當在屏幕內時,讓其一直覆蓋在占位的tablayout 上,看上去是跟着scrollview 一起滑動的;當滑出屏幕時,實際的tablayout 不斷移動 使其相對屏幕靜止,看上去是吸附在屏幕頂部。
scrollView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
isScroll = true;
}
return false;
}
});
//監聽scrollview滑動
scrollView.setCallbacks(new CustomScrollView.Callbacks() {
@Override
public void onScrollChanged(int x, int y, int oldx, int oldy) {
//根據滑動的距離y(不斷變化的) 和 holderTabLayout距離父布局頂部的距離(這個距離是固定的)對比,
//當y < holderTabLayout.getTop()時,holderTabLayout 仍在屏幕內,realTabLayout不斷移動holderTabLayout.getTop()距離,覆蓋holderTabLayout
//當y > holderTabLayout.getTop()時,holderTabLayout 移出,realTabLayout不斷移動y,相對的停留在頂部,看上去是靜止的
int translation = Math.max(y, holderTabLayout.getTop());
realTabLayout.setTranslationY(translation);
if (isScroll) {
for (int i = tabTxt.length - 1; i >= 0; i--) {
//需要y減去頂部內容區域的高度(具體看項目的高度,這里demo寫死的200dp)
if (y - 200 * 3 > anchorList.get(i).getTop() - 10) {
setScrollPos(i);
break;
}
}
}
}
});
private void setScrollPos(int newPos) {
if (lastPos != newPos) {
realTabLayout.setScrollPosition(newPos, 0, true);
}
lastPos = newPos;
}
tablayout點擊切換
由於實際操作的是realtablayout ,所以這里只需要一直監聽該tablayout。
//實際的tablayout的點擊切換
realTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
isScroll = false;
int pos = tab.getPosition();
int top = anchorList.get(pos).getTop();
//同樣這里滑動要加上頂部內容區域的高度(這里寫死的高度)
scrollView.smoothScrollTo(0, top + 200 * 3);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
至此,滑動定位+頂部吸附懸停 的效果結束了。做完之后,再看這個效果,其實和 支付寶-首頁 更多 那個頁面里的滑動效果一樣。
代碼與之前文章的在同一個git地址里。
詳細代碼見
github地址:https://github.com/taixiang/tabScroll
歡迎關注我的博客:https://blog.manjiexiang.cn/
更多精彩歡迎關注微信號:春風十里不如認識你
有個「佛系碼農圈」,歡迎大家加入暢聊,開心就好!
過期了,可加我微信 tx467220125 拉你入群。