1. 程式人生 > >android之用scrollview實現控制元件滑動固定效果

android之用scrollview實現控制元件滑動固定效果

專案中最近用到需要佈局滑動到某一個地方的時候某個控制元件固定在螢幕頂部不動,就去研究了下,思路其實挺簡單的。我置頂的懸浮控制元件上邊還需要留個控制元件,比如搜尋框之類的,專案需求不一樣就留的不一樣,所以就研究了一下,網上也有很多,其實方法思路都一樣的,很簡單,自定義一下ScrollView就可以了。

然後自己就寫了下,效果圖如下:


哎喲,廢話說多了心口疼,不廢話了直接開始吧,首先我們每次做一個功能前需要分析怎麼實現,我們要的步驟:

(1)需要知道這個scrollview滑動了多少,因為我們是根據滑動距離來判斷是否讓綠色那個框停在當前頁的上面,那麼我們需要滑動多少才讓他停下來呢?看下圖:

我們需要:手指上滑時當綠色框的頂部從位置3滑動到位置2時就停靠在位置2那裡了;下滑時綠色框的頂部到達位置2的時候停靠的綠色框不停靠

(2)怎麼停靠?我們可以讓整個介面佈局使用relativelayout,讓搜尋那個框在最上面,然後需要停靠的綠色框隱藏在搜尋框下面,簡而言之就是2個綠色框了,一個是在scrollview裡面即上圖中看見的綠色框(簡稱內部綠色框),一個是在搜尋欄下面隱藏著的(簡稱外部綠色框),當我們此時上滑時,內部綠色框從位置3到位置2我們就顯示外部綠色框;反之隱藏

(3)獲取位置3到位置2的距離distance,然後與scrollview滑動的距離scrollY進行比較,如果scrllY大於或等於distance,那麼就說明我們從位置3滑動到了位置2或者位置2的上面,那麼我們就需要顯示外部綠色框,如果小於說明還沒到達位置2,那麼我們就不顯示外部綠色框

好了,說了這麼多應該能聽懂吧,你要是聽不懂。。。。。。。。好吧,怪我咯,說不清楚,我的錯。下面那就直接開始程式碼吧

首先自定義MyScrollView獲取滑動的距離利用scrollview的getscrollY方法獲取滑動的距離,那有人就問了,既然scrollview有這個方法,為什麼要重寫呢,直接在外面用scrollview.getScrollY()不就行了。那麼。。。可以是可以,但是大鍋,我們需要隨時獲取他的高度,而不是我們執行一次getScrollY就獲取一次,我們需要在滑動過程中隨時隨地獲取他的高度來進行比較,不然你在外面用scrollview.getScrollY()方法,怎麼寫?用handler每x毫秒獲取一次?那也許恰好在你2次獲取的間隔中我到達了位置2呢,而且你的handler什麼時候開始執行?什麼時候不執行,還有如果我瞬滑(別問我瞬滑是誰,你問靜靜去)呢,那我手指離開了但是scrollview還在滑動怎麼辦。。。不廢話了,懂了吧,所以我們需要在scrollview的onTouchEvent方法中監聽他的touch事件,只要你要滑動,那你肯定要touch這個scrollview,所以我們在他的onTouchEvent方法中獲取滑動距離是最好的,只要onTouchEvent方法被觸發那就執行getscrollY(),還要加一個特殊情況,如果手指離開了,即ACTION_UP,那麼我們需要隔一會兒就獲取一下距離,然後跟手指離開時的距離進行比較,如果不一樣,那就說明手指放開後還滑動了,那就再更新scrollY的值,把scrollY的值通過自己寫的interface設定listener傳給activity就行了,思路說完了,程式碼直接貼上來了:

package com.custom.my.view;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ScrollView;

public class MyScrollView extends ScrollView {

    private  MyScrollListener listener;
    private int scrollDistanceY;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if(msg.what == 1){
                int newY = getScrollY();  //獲取手指離開後再次更新獲取的Y值
                if(newY != scrollDistanceY){ //如果不等與手指離開後的Y值,說明滑動還沒停止,繼續獲取新值
                    scrollDistanceY = newY;  //將新值賦給引數
                    listener.sendDistanceY(scrollDistanceY);  //傳遞新值
                    handler.sendMessageDelayed(handler.obtainMessage(), 5);  //滑動沒停止,再次獲取
                }
            }
        }
    };

    public MyScrollView(Context context) {
        super(context);
    }

    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public interface MyScrollListener{  //介面,用於傳遞滑動的Y值資料,activity實現該介面即可獲取該值
        void sendDistanceY(int distance);
    }

    public void setMyScrollListener(MyScrollListener myScrollListener){  //繫結
        this.listener = myScrollListener;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {  //事件處理
        if(ev.getAction() == MotionEvent.ACTION_UP){  //如果手指離開,就30毫秒後再次請求,其他操作不影響,都傳遞滑動距離即可
            handler.sendEmptyMessageDelayed(1,30);  //30毫秒後再次執行,以防手指離開後還在繼續滑動
        }
        scrollDistanceY = getScrollY();   //獲取滑動的Y值
        listener.sendDistanceY(scrollDistanceY);//傳遞Y值
        return super.onTouchEvent(ev);
    }
}

上面自定義的MyScrollview,該說明的都說了,下面是佈局檔案:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.custom.my.activity.ScrollTest">

    <com.custom.my.view.MyScrollView
        android:id="@+id/scv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <ImageView
                android:id="@+id/img"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:scaleType="centerCrop"
                android:src="@drawable/ic_mine_head_background"/>
            <TextView
                android:id="@+id/move"
                android:layout_width="match_parent"
                android:layout_height="35dp"
                android:gravity="center"
                android:background="#00ff00"
                android:text="老子就是要動,不服打我"/>
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:scaleType="centerCrop"
                android:src="@drawable/ic_mine_head_background"/>
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:scaleType="centerCrop"
                android:src="@drawable/ic_mine_head_background"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:gravity="center"
                android:background="#50ff0000"
                android:text="圖片瀏覽"/>
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:scaleType="centerCrop"
                android:src="@drawable/ic_mine_head_background"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:gravity="center"
                android:background="#50ff0000"
                android:text="圖片瀏覽"/>
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:scaleType="centerCrop"
                android:src="@drawable/ic_mine_head_background"/>
        </LinearLayout>
    </com.custom.my.view.MyScrollView>


    <TextView
        android:id="@+id/head"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:background="#000000"
        android:textColor="#ffffff"
        android:text="打死我也不會動的,我是搜尋哥(搜尋按鈕)"/>
    <TextView
        android:id="@+id/stop"
        android:visibility="gone"
        android:layout_below="@+id/head"
        android:layout_width="match_parent"
        android:layout_height="35dp"
        android:gravity="center"
        android:background="#00ff00"
        android:text="老子就是要動,不服打我"/>

</RelativeLayout>

佈局是基礎,就不多廢話了,你要我多廢話我也不多,口水不夠啊。

接下來就是activity的操作了:

head表示頭部搜尋欄,stop表示外面綠色欄即需要停靠的那個,move表示在scrollview裡面需要跟著滑動的內部綠色欄,myScrollView表示自定義的MyscrollView

那麼,當前activity implements MyscrollView.MyScrollListener,實現該介面獲取滑動的Y值

myScrollView.setMyScrollListener(this);  //繫結

接下來需要獲取位置2和位置3之間的距離,如何獲取呢?很簡單,獲取位置3的到頂部的距離減去位置2到頂部的距離,那麼

我們需要重寫activity的 onWindowFocusChanged方法,在該方法中獲取位置3和位置2:

@Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if(hasFocus){
            topDistance = move.getTop();  //獲取位置3,即內部綠色欄的頂部到佈局頂部的距離
            height = head.getMeasuredHeight();  //獲取位置2,不就是搜尋欄的高度麼,啊哈哈哈,是不是很機智,當然你也可以用getButtom,一樣的,看你自己
        }
    }

接下來實現了MyscrollView.MyScrollListener這個,我們可以得到滑動的距離了:

@Override
    public void sendDistanceY(int scrollY) {
        Log.d("scroll","----------------------height:"+height);
        if(scrollY >= topDistance - height){  //如果滑動的距離大於或等於位置3到位置2的距離,那麼說明內部綠色的頂部在位置2上面了,我們需要顯示外部綠色欄了
            stop.setVisibility(View.VISIBLE);
        }else {  //反之隱藏
            stop.setVisibility(View.GONE);
        }
    }

好了,說完了,看著廢話多,其實很簡單,思路很簡單,不過怕新手看不懂,廢話寫得有點多了,而且初發部落格,不大有經驗,大家別噴,在下趙日天謝過了。

補充1:有人可能會問為什麼要用一個外部和一個內部呢,用一個不行麼?答案是:當然可以,但是你如果用一個的話,當你外部綠色按鈕顯示的時候你的內部就刪除了,這樣你的整個layout的高度就變了,你試試會發現佈局突然變矮了一截,看著很不流暢(外部顯示的時候,就addview到外部,內部就用removeview刪除),看你自己了,當然還有其他方法,比如用一個空的view佔高之類的,反正你自己看著辦咯,我也管不了咯,你愛怎麼樣就怎麼樣咯

補充2:獲取高度啊,到頂部距離啊之類的,記住在onWindowFocusChanged方法中獲取,如果在oncreate中獲取永遠是0,因為還沒繪製完成,當然你也可以在onGlobalLayout中獲取,看你自己了

注意:

if(ev.getAction() == MotionEvent.ACTION_UP){  //如果手指離開,隔30就再執行請求獲取Y值看是否跟手指離開時的Y值一致
            handler.sendEmptyMessageDelayed(1,30);
        }
在該方法中handler.sendEmptyMessageDelayed(1,30);這個30都懂吧,意思是延遲操作,儘量別寫太小比如1-10之內的,為什麼呢,因為這個是手指離開後30毫秒再次獲取高度的,如果時間太短,比如1毫秒,也許恰好1毫秒後還在繼續滑動,而獲取的高度和手指離開時的高度一樣,這樣就會停止更新獲取,導致資料不準確,總而言之就是,間隔太短導致資料超短時間內一致,資料就不準確了,當然也不要太長了,為啥就不用我講了吧。好累,我得回去補補腎了

最後最後。。。。。。特麼的基本不用部落格,寫這個改了我好幾遍才正常,都是淚


相關推薦

androidscrollview實現控制元件滑動固定效果

專案中最近用到需要佈局滑動到某一個地方的時候某個控制元件固定在螢幕頂部不動,就去研究了下,思路其實挺簡單的。我置頂的懸浮控制元件上邊還需要留個控制元件,比如搜尋框之類的,專案需求不一樣就留的不一樣,所以就研究了一下,網上也有很多,其實方法思路都一樣的,很簡單,自定義一下Sc

iOSxib給控制元件設定圓角、邊框效果

轉自:https://www.cnblogs.com/zhun/p/5616540.html   xib中為各種控制元件設定圓角 通過程式碼的方式設定 @interface ViewController () @property (weak, nonatomic)

androidToolbar取消子控制元件左邊留白

android之Toolbar/Actionbar取消子控制元件左邊留白 1.xml檔案裡面直接設定 <android.support.v7.widget.Toolbar     android:layout_width="match_parent"  

jQuery---jq實現控制元件的顯示和隱藏

因為發現這個功能用的比較頻繁,所以分享一下: 先說一下,隱藏控制元件有兩種方式:style="visibility: hidden;"和style="display: none;",個人比較喜歡使用style="display: none;",因為style="visibility: h

android自定義組合控制元件

---------------setting_item---------------<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.an

Android 點選按鈕實現控制元件顯示隱藏

我寫了一個自定義的listview,listview 每一列點選切換圖示 同時顯示 隱藏的佈局,再次點選則隱藏該佈局。以下是判斷的程式碼: holder.isShowlin.setOnClickListener(new View.OnClickListener() { @

androidViewPager簡單實現區域性頁面滑動效果

-Viewpager能實現什麼效果? -實現左右滑動,切換view的效果。 -既可以實現整個頁面左右滑動,也可以實現同一個頁面中區域性左右滑動。 搞清楚viewpager的作用後,開始寫一個簡單例子,實現同一個頁面中區域性滑動的效果。 在coding前要做的準備工作 2

Android日期時間選擇控制元件DatePicker和TimePicker

這個月根據需求在專案中做了一個時間選擇器,雖然沒有用到Android原生的時間選擇控制元件,但我羞愧地發現自己竟然從來沒有用過這方面控制元件!趁現在有時間,趕緊查缺補漏,寫一篇部落格吧。 (注:為了便於區分,本文將選擇年月日的控制元件稱為日期選擇控制元件,將選

IOS ScrollView控制元件滑動手勢衝突

允許子檢視手勢延時響應 delaysContentTouches設 置為YES, CanCancelContentTouches設定為NO 以上設定了只是達到停頓0.5秒後,子控制元件可以手勢拖動

android RadioButton單選控制元件

示例程式碼:前端程式碼:<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schem

AndroidHandler實現主執行緒和子執行緒互相通訊以及子執行緒和子執行緒之間的通訊

1、上程式碼 activity_main.xml檔案 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.

android 自定義ScrollView實現背景圖片伸縮(阻尼效果)

android 自定義ScrollView實現強調內容背景圖片伸縮(仿多米,qq空間背景的重新整理) bug修改:只需將自定義PersonalScrollView的真假判斷修改即可,修改後的程式碼片段如下: case MotionEven

Android學習路------自定義控制元件,圓形進度條的簡單實現

簡單介紹 主要是通過自定義一個view類,然後通過操作canvas和paint進行效果的實現 Step 1 新建一個attr.xml,這裡主要是為了自定義我們的控制元件屬性,attr開頭的語句表示控制元件的自定義屬性,在這裡為了實現圓形進度條,定義了一

WPFS資料繫結(要是後臺類物件的屬性值發生改變,通知在“客戶端介面與繫結的控制元件值”也發生改變需要實現INotitypropertyChanged介面)

WPFS資料繫結(要是後臺類物件的屬性值發生改變,通知在“客戶端介面與之繫結的控制元件值”也發生改變需要實現INotitypropertyChanged介面) MainWindow.xaml 1 <Window x:Class="WpfApplication1.MainWindow" 2

android 禁止scrollview控制元件變化自動滾動到底的方法

網上的說法是焦點問題導致,新焦點在下面時,檢視會滾動使焦點可見。但網上提的在scrollview裡層的LinearLayout裡新增    android:focusable="true" android:focusableIn

Android工程師開發iOSAndroid中對應功能的控制元件

iOS中和安卓控制元件應用還是差不的,從控制元件的命名上我們就能看出來,下面來介紹一下吧: ios中UILabel 對應Android 中TextView ios中UIImage對應Android 中ImageView 圖片控制元件 ios中UIButton對應An

android自定義星級評分控制元件,可實現只顯示實心星星

話不多說,上圖 近日app需求弄一個等級展示,看了下UI圖,只顯示實星(點亮的星星).如圖 但是網上關於星級評分的例子大多這樣 也展示虛心星星 通過自定義View package com.starsbar; import android.content.C

android xml實現控制元件邊框陰影漸變效果

實現原理:使用兩塊畫布重疊,上面畫布小於下面畫布,下面畫布漸變 <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/an

繼承式自定義控制元件——滑動ScrollView,標題顏色漸變

MainActivity.java public class MainActivity extends AppCompatActivity { private ImageView mIvDetail; private ObservableScr

Android開發RadioGroup與RadioButton控制元件使用

      RadioButton即單選按鈕,它在開發中提供了一種“多選一”的操作模式,是Android開發中常用的一種元件,例如在使用者註冊時,選擇性別時只能從“男”或者“女”中選擇一個。與Web開發不同的是,在Android中可以使用RadioGroup來定義單選按鈕元件