相信做前端的都做過頁面錨點定位的功能,通過`` 去設置頁面內錨點定位跳轉。 本篇文章就使用 、`scrollview`來實現android錨點定位的功能。 效果圖: 實現思路 1、監聽 滑動到的位置, 切換到對應標簽 2、 各標簽點擊, 可滑動到對應區域 自定義scrollview 因為我們需要監聽 ...
相信做前端的都做過頁面錨點定位的功能,通過<a href="#head">
去設置頁面內錨點定位跳轉。
本篇文章就使用tablayout
、scrollview
來實現android錨點定位的功能。
效果圖:
實現思路
1、監聽scrollview
滑動到的位置,tablayout
切換到對應標簽
2、tablayout
各標簽點擊,scrollview
可滑動到對應區域
自定義scrollview
因為我們需要監聽到滑動過程中scrollview
的滑動距離,自定義scrollview
通過介面暴露滑動的距離。
public class CustomScrollView extends ScrollView {
public Callbacks mCallbacks;
public CustomScrollView(Context context) {
super(context);
}
public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setCallbacks(Callbacks callbacks) {
this.mCallbacks = callbacks;
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (mCallbacks != null) {
mCallbacks.onScrollChanged(l, t, oldl, oldt);
}
}
//定義介面用於回調
public interface Callbacks {
void onScrollChanged(int x, int y, int oldx, int oldy);
}
}
佈局文件里 tablayout
和 CustomScrollView
,內容暫時使用LinearLayout
填充。
<?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">
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="@color/colorPrimary"
app:tabMode="scrollable"
app:tabSelectedTextColor="@color/colorPrimary" />
<com.tabscroll.CustomScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:fitsSystemWindows="true">
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
</LinearLayout>
</com.tabscroll.CustomScrollView>
</LinearLayout>
數據模擬
數據模擬,動態添加scrollview
內的內容,這裡自定義了AnchorView
當作每一塊的填充內容。
private String[] tabTxt = {"客廳", "卧室", "餐廳", "書房", "陽臺", "兒童房"};
//內容塊view的集合
private List<AnchorView> anchorList = new ArrayList<>();
//判讀是否是scrollview主動引起的滑動,true-是,false-否,由tablayout引起的
private boolean isScroll;
//記錄上一次位置,防止在同一內容塊里滑動 重覆定位到tablayout
private int lastPos;
//模擬數據,填充scrollview
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);
}
//tablayout設置標簽
for (int i = 0; i < tabTxt.length; i++) {
tabLayout.addTab(tabLayout.newTab().setText(tabTxt[i]));
}
定義變數標誌isScroll
,用於判斷scrollview
的滑動由誰引起的,避免通過點擊tabLayout
引起的scrollview滑動問題。
定義變數標誌lastPos
,當scrollview
在同一模塊中滑動時,則不再去調用tabLayout.setScrollPosition
刷新標簽。
自定義的AnchorView
:
public class AnchorView extends LinearLayout {
private TextView tvAnchor;
private TextView tvContent;
public AnchorView(Context context) {
this(context, null);
}
public AnchorView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public AnchorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
View view = LayoutInflater.from(context).inflate(R.layout.view_anchor, this, true);
tvAnchor = view.findViewById(R.id.tv_anchor);
tvContent = view.findViewById(R.id.tv_content);
Random random = new Random();
int r = random.nextInt(256);
int g = random.nextInt(256);
int b = random.nextInt(256);
tvContent.setBackgroundColor(Color.rgb(r, g, b));
}
public void setAnchorTxt(String txt) {
tvAnchor.setText(txt);
}
public void setContentTxt(String txt) {
tvContent.setText(txt);
}
}
實現
scrollview
的滑動監聽:
scrollView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//當由scrollview觸發時,isScroll 置true
if (event.getAction() == MotionEvent.ACTION_DOWN) {
isScroll = true;
}
return false;
}
});
scrollView.setCallbacks(new CustomScrollView.Callbacks() {
@Override
public void onScrollChanged(int x, int y, int oldx, int oldy) {
if (isScroll) {
for (int i = tabTxt.length - 1; i >= 0; i--) {
//根據滑動距離,對比各模塊距離父佈局頂部的高度判斷
if (y > anchorList.get(i).getTop() - 10) {
setScrollPos(i);
break;
}
}
}
}
});
//tablayout對應標簽的切換
private void setScrollPos(int newPos) {
if (lastPos != newPos) {
//該方法不會觸發tablayout 的onTabSelected 監聽
tabLayout.setScrollPosition(newPos, 0, true);
}
lastPos = newPos;
}
tabLayout
的點擊切換:
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
//點擊標簽,使scrollview滑動,isScroll置false
isScroll = false;
int pos = tab.getPosition();
int top = anchorList.get(pos).getTop();
scrollView.smoothScrollTo(0, top);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
至此效果出來了,但是
問題來了 可以看到當點擊最後一項時,scrollView
滑動到底部時並沒有呈現出我們想要的效果,希望滑到最後一個時,全屏只有最後一塊內容顯示。
所以這裡需要處理下最後一個view的高度,當不滿全屏時,重新設置他的高度,通過計算讓其撐滿屏幕。
//監聽判斷最後一個模塊的高度,不滿一屏時讓最後一個模塊撐滿屏幕
private ViewTreeObserver.OnGlobalLayoutListener listener;
listener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int screenH = getScreenHeight();
int statusBarH = getStatusBarHeight(MainActivity.this);
int tabH = tabLayout.getHeight();
//計算內容塊所在的高度,全屏高度-狀態欄高度-tablayout的高度-內容container的padding 16dp
int lastH = screenH - statusBarH - tabH - 16 * 3;
AnchorView lastView = anchorList.get(anchorList.size() - 1);
//當最後一個view 高度小於內容塊高度時,設置其高度撐滿
if (lastView.getHeight() < lastH) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.height = lastH;
lastView.setLayoutParams(params);
}
container.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
};
container.getViewTreeObserver().addOnGlobalLayoutListener(listener);
這樣就達到了預期的效果了。
寫到這裡,tablayout + scrollview的錨點定位成型了,在實際項目中,我們還可以使用tablayout + recyclerview 來完成同樣的效果,後續的話會帶來這樣的文章。
這段時間自己在做一個小程式,包括數據爬取 + 後臺 + 小程式的,可能要過段時間才能出來,主要是數據爬蟲那邊比較麻煩的...期待下!
詳細代碼見
github地址:https://github.com/taixiang/tabScroll
歡迎關註我的博客:https://blog.manjiexiang.cn/
更多精彩歡迎關註微信號:春風十里不如認識你
有個「佛系碼農圈」,歡迎大家加入暢聊,開心就好!
過期了,可加我微信 tx467220125 拉你入群。