您好,登錄后才能下訂單哦!
完整工程代碼在這:https://github.com/NashLegend/Auto-Hide-ListView
現在很多軟件都有這種滑動列表的時候自動隱藏頁面頭部和底部元素的功能,比如Google+。在剛剛進入Activity的時候,頁面是一個列表,底部有一個view,頭部一個view,當列表向上滑動的時候,隱藏頭尾元素,以顯示更多內容,當列表向下滑動的時候,再將頭尾元素拉出來。比如Google+。
剛剛進入時是這個樣子:
再把列表身上一拉,頭尾隱藏,成了這個樣子:
再往下拉,就會再變回第一張圖的樣子。
這個例子實現的就是這個功能
這個例子里面,MainActivity的布局如下,ToolBar是頂部元素,Button為底部元素。
<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=".MainActivity"> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent" android:headerDividersEnabled="false" /> <android.support.v7.widget.Toolbar android:id="@+id/action_bar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@android:color/holo_blue_light" /> <Button android:id="@+id/footer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:text="@string/ScrollDown" /> </RelativeLayout>
public class MainActivity extends ActionBarActivity { ListView listView; Toolbar toolbar; View header; View footer; int touchSlop = 10; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); touchSlop = (int) (ViewConfiguration.get(MainActivity.this).getScaledTouchSlop() * 0.9);//滾動過多少距離后才開始計算是否隱藏/顯示頭尾元素。這里用了默認touchslop的0.9倍。 listView = (ListView) findViewById(R.id.list_view); footer = findViewById(R.id.footer); toolbar = (Toolbar) findViewById(R.id.action_bar); // 下面這句將這個ToolBar設置為ActionBar,在這個例子里面,這句其實用不著,但是如果用了這句,就得把Theme設置為NoActionBar了,無關這里要說的,具體見上面的鏈接中的Style setSupportActionBar(toolbar); //為這個ListView填充元素。 String[] str = new String[64]; for (int i = 0; i < str.length; i++) { str[i] = "Android " + i; } //R.layout.simple_layout是一個TextView,詳見上面的鏈接…… ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, R.layout.simple_layout, str); listView.setAdapter(adapter); //為ListView添加一個Header,這個Header與ToolBar一樣高。這樣我們可以正確的看到列表中的第一個元素而不被遮住。 header = new View(MainActivity.this); header.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) getResources().getDimension(R.dimen.abc_action_bar_default_height_material))); header.setBackgroundColor(Color.parseColor("#00000000")); listView.addHeaderView(header); //為ListView設置觸摸事件和滾動事件,這是核心 listView.setOnTouchListener(onTouchListener); listView.setOnScrollListener(onScrollListener); footer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //為button設置點擊事件,點擊一次滾動10個item listView.smoothScrollByOffset(10); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } AnimatorSet backAnimatorSet;//這是顯示頭尾元素使用的動畫 private void animateBack() { //先清除其他動畫 if (hideAnimatorSet != null && hideAnimatorSet.isRunning()) { hideAnimatorSet.cancel(); } if (backAnimatorSet != null && backAnimatorSet.isRunning()) { //如果這個動畫已經在運行了,就不管它 } else { backAnimatorSet = new AnimatorSet(); //下面兩句是將頭尾元素放回初始位置。 ObjectAnimator headerAnimator = ObjectAnimator.ofFloat(toolbar, "translationY", toolbar.getTranslationY(), 0f); ObjectAnimator footerAnimator = ObjectAnimator.ofFloat(footer, "translationY", footer.getTranslationY(), 0f); ArrayList<Animator> animators = new ArrayList<>(); animators.add(headerAnimator); animators.add(footerAnimator); backAnimatorSet.setDuration(300); backAnimatorSet.playTogether(animators); backAnimatorSet.start(); } } AnimatorSet hideAnimatorSet;//這是隱藏頭尾元素使用的動畫 private void animateHide() { //先清除其他動畫 if (backAnimatorSet != null && backAnimatorSet.isRunning()) { backAnimatorSet.cancel(); } if (hideAnimatorSet != null && hideAnimatorSet.isRunning()) { //如果這個動畫已經在運行了,就不管它 } else { hideAnimatorSet = new AnimatorSet(); ObjectAnimator headerAnimator = ObjectAnimator.ofFloat(toolbar, "translationY", toolbar.getTranslationY(), -toolbar.getHeight());//將ToolBar隱藏到上面 ObjectAnimator footerAnimator = ObjectAnimator.ofFloat(footer, "translationY", footer.getTranslationY(), footer.getHeight());//將Button隱藏到下面 ArrayList<Animator> animators = new ArrayList<>(); animators.add(headerAnimator); animators.add(footerAnimator); hideAnimatorSet.setDuration(200); hideAnimatorSet.playTogether(animators); hideAnimatorSet.start(); } } View.OnTouchListener onTouchListener = new View.OnTouchListener() { float lastY = 0f; float currentY = 0f; //下面兩個表示滑動的方向,大于0表示向下滑動,小于0表示向上滑動,等于0表示未滑動 int lastDirection = 0; int currentDirection = 0; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastY = event.getY(); currentY = event.getY(); currentDirection = 0; lastDirection = 0; break; case MotionEvent.ACTION_MOVE: if (listView.getFirstVisiblePosition() > 0) { //只有在listView.getFirstVisiblePosition()>0的時候才判斷是否進行顯隱動畫。因為listView.getFirstVisiblePosition()==0時, //ToolBar——也就是頭部元素必須是可見的,如果這時候隱藏了起來,那么占位置用了headerview就被用戶發現了 //但是當用戶將列表向下拉露出列表的headerview的時候,應該要讓頭尾元素再次出現才對——這個判斷寫在了后面onScrollListener里面…… float tmpCurrentY = event.getY(); if (Math.abs(tmpCurrentY - lastY) > touchSlop) {//滑動距離大于touchslop時才進行判斷 currentY = tmpCurrentY; currentDirection = (int) (currentY - lastY); if (lastDirection != currentDirection) { //如果與上次方向不同,則執行顯/隱動畫 if (currentDirection < 0) { animateHide(); } else { animateBack(); } } lastY = currentY; } } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: //手指抬起的時候要把currentDirection設置為0,這樣下次不管向哪拉,都與當前的不同(其實在ACTION_DOWN里寫了之后這里就用不著了……) currentDirection = 0; lastDirection = 0; break; } return false; } }; AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() { //這個Listener其實是用來對付當用戶的手離開列表后列表仍然在滑動的情況,也就是SCROLL_STATE_FLING int lastPosition = 0;//上次滾動到的第一個可見元素在listview里的位置——firstVisibleItem int state = SCROLL_STATE_IDLE; @Override public void onScrollStateChanged(AbsListView view, int scrollState) { //記錄當前列表狀態 state = scrollState; } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (firstVisibleItem == 0) { animateBack(); } if (firstVisibleItem > 0) { if (firstVisibleItem > lastPosition && state == SCROLL_STATE_FLING) { //如果上次的位置小于當前位置,那么隱藏頭尾元素 animateHide(); } //================================ if (firstVisibleItem < lastPosition && state == SCROLL_STATE_FLING) { //如果上次的位置大于當前位置,那么顯示頭尾元素,其實本例中,這個if沒用 //如果是滑動ListView觸發的,那么,animateBack()肯定已經執行過了,所以沒有必要 //如果是點擊按鈕啥的觸發滾動,那么根據設計原則,按鈕肯定是頭尾元素之一,所以也不需要animateBack() //所以這個if塊是不需要的 animateBack(); } //這里沒有判斷(firstVisibleItem == lastPosition && state == SCROLL_STATE_FLING)的情況, //但是如果列表中的單個item如果很長的話還是要判斷的,只不過代碼又要多幾行 //但是可以取巧一下,在觸發滑動的時候拖動執行一下animateHide()或者animateBack()——本例中的話就寫在那個點擊事件里就可以了) //BTW,如果列表的滑動純是靠手滑動列表,而沒有類似于點擊一個按鈕滾到某個位置的話,只要第一個if就夠了… } lastPosition = firstVisibleItem; } }; }
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。