Android Listview多tab上滑懸浮效果
樣例
近期要做一個含有兩個tab切換頁面,兩個頁面有公共的描述信息區(qū)域,兩個tab都是listview,可以向上或向下拉動刷新,在頁面中部有一個tab切換區(qū)域,向上滑動的時候tab區(qū)域到頂部后就不在移動,向下拉又重新回到初始位置,先看一樣樣式圖吧!

整個需求大致如上圖所示,其中上拉刷新和下拉刷新沒有截圖,采用了開源控件PullToRefreshListView來實現(xiàn)這個效果。
實現(xiàn)方式
總體思路,為了簡單不想監(jiān)控很多手勢問題,因此投機取巧的采用下面的方式來實現(xiàn),
a. 整個頁面是一個listview,公共的區(qū)域作為listview的header添加進來,兩個切換的tab也作為一個header加入,
b. 在頁面布局的時候在listview上面添加一層,里面放tab的布局,這個tab的布局與listview的header中的是同一個布局,
c. 之后當(dāng)listview滑動時候在onScroll函數(shù)中處理頁面tab布局的顯示與隱藏,當(dāng)listview的tab布局到達屏幕頂部時,顯示頁面中的tab布局,向下滑動當(dāng)整個tab都出現(xiàn)是影藏界面中的tab布局
d. tab切換,由于tab1,tab2的數(shù)據(jù)不同,因此采用了三個數(shù)據(jù)源,在tab切換的時候,數(shù)據(jù)來回切換,當(dāng)點擊tab時,記住當(dāng)前顯示的tab的pos和偏移量(只記住pos重定位的時候會有偏差)
demo的大致流程就是這樣了,沒有添加刷新的處理,雖然實際項目中時處理了更多的邏輯,但是demo不想寫太復(fù)雜(主要是沒有人看,就自己看,稍稍寫寫)。
說了這么多,可能看的人的還是不怎么明白,下面就來看代碼吧
首先是界面布局,底層一個listview,頂部一個tab布局,界面布局up_float_first_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/white_color" >
<com.example.toolbox.upFloat.PullToRefreshListView
xmlns:ptr="http://schemas.android.com/apk/res-auto"
android:id="@+id/up_float_listview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="@color/white_color"
android:divider="@color/transpant"
android:dividerHeight="0dip"
android:fadingEdge="none"
android:fastScrollEnabled="false"
android:listSelector="@color/transpant"
android:smoothScrollbar="true"
android:visibility="visible"
ptr:ptrHeaderTextColor="@color/color_333333"
ptr:ptrMode="both" />
<include
layout="@layout/up_float_tab_layout"
android:visibility="gone" />
</FrameLayout>
tab布局,up_float_tab_layout.xml,text都采用了selector,這樣在選中時可以高亮顯示
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/up_float_tab_root"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/white_color"
android:minHeight="44dip"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="44dip"
android:minHeight="44dip"
android:orientation="horizontal" >
<TextView
android:id="@+id/up_fload_tab1"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/show_event_detail_tab_selector"
android:gravity="center"
android:text="@string/up_float_tab1"
android:textColor="@color/show_event_detail_tab_text_selector"
android:textSize="17sp" />
<TextView
android:id="@+id/up_float_tab2"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/show_event_detail_tab_selector"
android:gravity="center"
android:text="@string/up_float_tab2"
android:textColor="@color/show_event_detail_tab_text_selector"
android:textSize="17sp" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/split_one_pixels"
android:background="@color/color_purple_bd6aff" />
</LinearLayout>
公共部分布局up_float_common_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/white_color"
android:orientation="vertical" >
<ImageView
android:id="@+id/show_event_detail_bg"
android:layout_width="fill_parent"
android:layout_height="125dip"
android:contentDescription="@string/empty"
android:scaleType="fitXY"
android:src="@drawable/pic1" />
<TextView
android:id="@+id/show_event_detail_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dip"
android:layout_marginLeft="15dip"
android:layout_marginRight="15dip"
android:layout_marginTop="24dip"
android:text="@string/up_float_desc"
android:textColor="@color/color_black_333333"
android:textSize="14sp" />
<View style="@style/horizontal_gray_divider" />
<View style="@style/horizontal_gray_divider" />
</LinearLayout>
接下來就是主頁面的代碼了
package com.example.toolbox.upFloat.activity;
import java.util.ArrayList;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import com.example.toolbox.R;
import com.example.toolbox.upFloat.PullToRefreshBase;
import com.example.toolbox.upFloat.PullToRefreshBase.OnRefreshListener2;
import com.example.toolbox.upFloat.PullToRefreshListView;
/**
*
*
* @author sunyoujun
*
*/
public class UpFloatFirstActivity extends ActionBarActivity implements OnClickListener {
public static final int TYPE_TAB_1 = 1;
public static final int TYPE_TBA_2 = 2;
private int tab2Pos = 0;
private int tab2OffsetY = 0;
private int tab1Pos = 0;
private int tab1OffsetY = 0;
private ArrayList<String> item = new ArrayList<String>();
private ArrayList<String> item1 = new ArrayList<String>();
private ArrayList<String> item2 = new ArrayList<String>();
protected PullToRefreshListView listView;
private LinearLayout titleView;
private LayoutInflater infater;
private LinearLayout titleTab;
private LinearLayout titleFloatTab;
private TextView latestTv;
private TextView latestFloatTv;
private TextView hotTv;
private TextView hotFloatTv;
private int currentType = TYPE_TAB_1;
private ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.up_float_first_activity);
findViews();
setViewsListener();
updateTabSelectState();
initData();
initListView();
}
private void findViews() {
listView = (PullToRefreshListView) findViewById(R.id.up_float_listview);
titleFloatTab = (LinearLayout) findViewById(R.id.up_float_tab_root);
latestFloatTv = (TextView) titleFloatTab.findViewById(R.id.up_float_tab2);
hotFloatTv = (TextView) titleFloatTab.findViewById(R.id.up_fload_tab1);
infater = LayoutInflater.from(this);
titleView = (LinearLayout) infater.inflate(R.layout.up_float_common_layout, null);
titleTab = (LinearLayout) infater.inflate(R.layout.up_float_tab_layout, null);
latestTv = (TextView) titleTab.findViewById(R.id.up_float_tab2);
hotTv = (TextView) titleTab.findViewById(R.id.up_fload_tab1);
}
private void setViewsListener() {
latestTv.setOnClickListener(this);
hotTv.setOnClickListener(this);
latestFloatTv.setOnClickListener(this);
hotFloatTv.setOnClickListener(this);
updateTabSelectState();
}
/**
* 更新tab欄選中狀態(tài)
*/
private void updateTabSelectState() {
boolean isTab1 = (currentType == TYPE_TAB_1);
hotTv.setSelected(isTab1);
hotFloatTv.setSelected(isTab1);
latestTv.setSelected(!isTab1);
latestFloatTv.setSelected(!isTab1);
}
private void initData() {
for (int i = 1; i <= 50; i++) {
item1.add("tab1-- item ---" + i);
item2.add("tab2-- item ---" + i);
}
}
private void initListView() {
setListViewListener();
listViewAddHeader();
listViewLoadData();
}
private void setListViewListener() {
listView.setOnRefreshListener(new OnRefreshListener2<ListView>() {
@Override
public void onPullDownToRefresh(PullToRefreshBase<ListView> refreshView) {
// loadNews();
}
@Override
public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) {
// loadOlds();
}
});
listView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem < 2) {// 懸浮tab出現(xiàn)時機,listview含有三個header
titleFloatTab.setVisibility(View.GONE);
} else
titleFloatTab.setVisibility(View.VISIBLE);
;
}
});
}
private void listViewAddHeader() {
listView.getRefreshableView().addHeaderView(titleView);
listView.getRefreshableView().addHeaderView(titleTab);
}
protected void listViewLoadData() {
item.clear();
item.addAll(item1);
adapter = new ArrayAdapter<String>(this, R.layout.list_item, android.R.id.text1, item);
listView.setAdapter(adapter);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.up_fload_tab1:
switchTabtList(true);
break;
case R.id.up_float_tab2:
switchTabtList(false);
break;
default:
break;
}
}
private void switchTabtList(boolean isTab1) {
if (isTab1) {
if (currentType == TYPE_TAB_1) {
return;// 說明點擊的是相同的活動列表,不用改變
} else {// tab2 switch tab1
tab2Pos = listView.getRefreshableView().getFirstVisiblePosition();
tab2OffsetY = getOffsetY();
currentType = TYPE_TAB_1;
item2.clear();
item2.addAll(item);
item.clear();
item.addAll(item1);
}
} else {
if (currentType == TYPE_TBA_2) {
return;
} else {// tab1 switch tab2
tab1Pos = listView.getRefreshableView().getFirstVisiblePosition();
tab1OffsetY = getOffsetY();
currentType = TYPE_TBA_2;
item1.clear();
item1.addAll(item);
item.clear();
item.addAll(item2);
}
}
updateTabSelectState();
relocationLastPos();
}
private int getOffsetY(){
View view = listView.getRefreshableView().getChildAt(0);
return view != null ? view.getTop() : 0;
}
/**
* 重新定位到上次的位置
*/
private void relocationLastPos() {
if (adapter != null) {
adapter.notifyDataSetChanged();
}
if (currentType == TYPE_TAB_1) {
listView.post(new Runnable() {
@Override
public void run() {
listView.getRefreshableView().setSelectionFromTop(tab1Pos, tab1OffsetY);
}
});
} else {
listView.post(new Runnable() {
@Override
public void run() {
listView.getRefreshableView().setSelectionFromTop(tab2Pos, tab2OffsetY);
}
});
}
}
}
總結(jié):
a 上面的demo只是實現(xiàn)了向上滑動的效果,其實有很大的局限性,兩個tab的item布局要一致,才能自由切換,其次是兩個tab不能左右滑動
b 上面的只適合兩個或者一個tab,再多要控制的變量狀態(tài)就更多,很容易出錯,并且上面還沒有包含刷新的效果,數(shù)據(jù)返回時不能僅僅是添加到item,而要判斷刷新tab與當(dāng)前顯示tab的關(guān)系。
c 看了其他的開源項目,之后如果有時間會寫一個demo,做真正的多個tab,并且能左右切換的效果。
ps: 鑒于還是有很多人要源代碼,我就在下一篇重新實現(xiàn)了現(xiàn)有的方式,并且附上了git的代碼地址,不要錯過。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android用Scroller實現(xiàn)一個可向上滑動的底部導(dǎo)航欄
本篇文章主要介紹了Android用Scroller實現(xiàn)一個可上滑的底部導(dǎo)航欄,具有一定的參考價值,有興趣的小伙伴們可以參考一下2017-07-07
Android自定義View實現(xiàn)體重表盤詳解流程
對于安卓程序員來說,自定義view簡直不要太重要,畢竟有很多功能,譬如圓形頭像這些,用單純的原生非常難以實現(xiàn),而用自定義view,簡直分分鐘2021-11-11
Android中findViewById獲取控件返回為空問題怎么解決
這篇文章主要介紹了Android中findViewById獲取控件返回為空問題怎么解決的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-06-06
Android TextView和ImageView簡單說明
Android TextView和ImageView簡單說明,需要的朋友可以參考一下2013-03-03
實例講解Android應(yīng)用中自定義組合控件的方法
這篇文章主要介紹了實例講解Android應(yīng)用中自定義組合控件的方法,通過例子講解了view組合控件及自定義屬性的用法,需要的朋友可以參考下2016-04-04
Android Jetpack組件庫LiveData源碼深入探究
LiveData是Jetpack組件的一部分,更多的時候是搭配ViewModel來使用,相對于Observable,LiveData的最大優(yōu)勢是其具有生命感知的,換句話說,LiveData可以保證只有在組件( Activity、Fragment、Service)處于活動生命周期狀態(tài)的時候才會更新數(shù)據(jù)2022-09-09

