Android 實現錨點定位

来源:https://www.cnblogs.com/taixiang/archive/2018/07/22/9351112.html
-Advertisement-
Play Games

相信做前端的都做過頁面錨點定位的功能,通過`` 去設置頁面內錨點定位跳轉。 本篇文章就使用 、`scrollview`來實現android錨點定位的功能。 效果圖: 實現思路 1、監聽 滑動到的位置, 切換到對應標簽 2、 各標簽點擊, 可滑動到對應區域 自定義scrollview 因為我們需要監聽 ...


相信做前端的都做過頁面錨點定位的功能,通過<a href="#head"> 去設置頁面內錨點定位跳轉。
本篇文章就使用tablayoutscrollview來實現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);
    }

}

佈局文件里 tablayoutCustomScrollView,內容暫時使用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/
更多精彩歡迎關註微信號:春風十里不如認識你
image.png

有個「佛系碼農圈」,歡迎大家加入暢聊,開心就好!

過期了,可加我微信 tx467220125 拉你入群。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. 數據準備 2. 用戶一個月總金額 3. 將月總金額表 自己連接 自己連接 4. 累計報表 4.1. 類似數據在MySQL資料庫查詢 4.2. Hive中運行 ...
  • 1.返回 每月最後一天訂單 使用EMONTH 對輸入的日期返回月末日期 類似動態條件 DATEDIFF(month, '19991231', orderdate) 相差多少月 從19991231到 orderdate之間先查多少月 DATEADD(month, DATEDIFF(month, '19 ...
  • 1. 建庫建表 2. 數據準備 相關數據 數據導入 3. 常用操作 3.1. 學生表基本查詢 查詢學號為95001, 95005, 95008的學生信息 查詢學號中有9500字元串的學生信息 查詢全體學生的學號與姓名 查詢學生的總人數 3.2. 成績表相關查詢 查詢選修了課程的學生姓名 計算1號課程 ...
  • 簡介 將查詢語句查詢的結果集作為數據插入到數據表中。 一、通過INSERT SELECT語句形式向表中添加數據 例如,創建一張新表AddressList來存儲班級學生的通訊錄信息,然後這些信息恰好存在學生表中,則可以從學生表中提取相關的數據插入建好的AddressList表中。 T-SQL語句如下: ...
  • 問題說明:使用的MySQL是5.1.37版本,用的mysql-connector-java-5.0.4.jar版本,在java文件中定義的欄位是Date類型,MySQL中定義的欄位類型是datetime類型的, 嘗試了以下方式都不成功,報的錯誤還是一個,方法如下: 1.第一個方法: // Date ...
  • Hive官方文檔:Home-UserDocumentation Hive DML官方文檔:LanguageManual DML 參考文章:Hive 用戶指南 1. Loading files into tables 當我們做Load操作是,hive不會做任何數據轉換,只是純複製/移動操作,將數據文件 ...
  • Hive官方文檔:Home-UserDocumentation Hive DDL官方文檔:LanguageManual DDL 參考文章:Hive 用戶指南 註意:各個語句的版本時間,有的是在 hive-1.2.1 之後才有的,這些語句我們在hive-1.2.1中是不能使用的。 註意:本文寫的都是常 ...
  • 28年前有人發明www microsoft技術開發人員lot 看論文可以看中國知網 微軟亞洲研究院 WWDC蘋果開發者大會上,蘋果都會發佈一些新的公司發展出的新的產品的新技術。iOS開發,用到的語言有:objective-c swift 還有cocoa touch框架 storyboarding(故 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...