仿淘寶,京東多級地址選擇器
效果和淘寶地址選擇一模一樣,放個GIF。
效果是上週寫出來的,gif錄的早,後面又把選中item置頂加上了。
先扯一下地址資料來源問題,必須是層層關聯,遞進關係,例如省級用 01,02,03,到了市級01001,縣區01001001,網上的資料大多數也是這個關係。
github上有個資料來源:中國省市區鎮村三級四級五級聯動地址資料
[新增] 全國省市區縣行政區劃查詢_易源資料
思路:
最開始,想用tablayout + viewpager 做,但是寫了一半發現用viewpager程式碼量太多,太複雜了(viewpager中放fragment,裡面再放recycleview),然後改用tablayout + recycleview ,好在recycleview部分程式碼不變,所以最開始也沒白寫。
所以這個效果是用tablayout + recycleview寫出來的,稍微提一下原生tablayout的用法(平時都用自定義的,原生都快忘了):
XML:
app:tabMode="scrollable" 從頭顯示,不會居中 android:scrollbars="none" 去掉滑動到頭的陰影效果
新增,刪除Tab:
tablayout .addTab(tablayout .newTab().setText("請選擇"), true); 新增tab,true表示立即選中新增的tab tablayout .getTabAt(position).setText(name); 給指定tab重新settext tablayout .removeTabAtposition); 刪除指定tab int tabCount = tablayout .getTabCount(); 獲取tab總數,注意這裡不能.getChildCount
其實這玩意兒就那麼點東西,tablayout的增加刪除,recycleview的重新繫結,還有的都是小細節。
使用者點選時,如果點選tablayout(紅色塊) 可以看為展示對應地址列表操作,這時候只需要重新整理recycleview,如果點選的是recycleview的item(綠色塊),則作為一個選擇地址操作,不僅要重新整理recycleview,還要新增刪除tab。
實施
看這個結構也知道沒多少東西。
佈局還是看一眼:
<?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="wrap_content"
android:background="#ffffff"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginTop="15dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="配送至"
android:textColor="#000000"
android:textSize="16sp" />
<ImageView
android:id="@+id/close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="15dp"
android:foreground="?android:attr/selectableItemBackground"
android:padding="10dp"
android:src="@mipmap/sic_close" />
</RelativeLayout>
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
app:tabBackground="@drawable/item_bg"
app:tabMode="scrollable"
app:tabSelectedTextColor="#000000"
app:tabTextAppearance="@style/ChooseView"
app:tabTextColor="#000000" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="300dp"
android:orientation="vertical">
<utils.LoadingUtil.NewLoadingView
android:id="@+id/loading"
android:layout_width="match_parent"
android:layout_height="300dp"
android:visibility="gone" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="300dp"
android:overScrollMode="never" />
</LinearLayout>
</LinearLayout>
NewLoadingView是站位view,載入中,網路錯誤,其他異常用到的。
彈窗我用的popupwindow,先建立一個:
private PopupWindow popupWindow;
private Context mContext;
private Activity mActivity;
private ImageView mIv_close;
private TabLayout mTabLayout;
private RecyclerView mRecyclerView;
private NewLoadingView mLoadingView;
//地址資料集合 資料來源
private List<AraeData> mAraeDatas;
//地址資料集合 結果集
private List<AraeDataResult> mResultList = new ArrayList<>();
//標示 用來判斷是否為最後一級資料
private boolean isLast = false;
//標示 用來區分是列表item點選 還是tab點選
private boolean itemClick = false;
//當前選中的tab
private int mTabCurrent = 0;
//地址選擇完成的監聽
private OnSelectOkListener mlistener;
//初始化
private void init() {
View pop_view = LayoutInflater.from(mContext).inflate(R.layout.address_choose_pop, null);
popupWindow = new PopupWindow(pop_view, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
// 點選其他區域關閉
popupWindow.setFocusable(true);
popupWindow.setOutsideTouchable(true);
popupWindow.setAnimationStyle(android.R.style.Animation_Toast);
popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
Window window = mActivity.getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.alpha = 1.0F;
window.setAttributes(params);
if (isLast) {
mlistener.Select(mResultList);
}
}
});
mIv_close = pop_view.findViewById(R.id.close);
mTabLayout = pop_view.findViewById(R.id.tablayout);
mLoadingView = pop_view.findViewById(R.id.loading);
mRecyclerView = pop_view.findViewById(R.id.recyclerview);
mIv_close.setOnClickListener(this);
mTabLayout.addOnTabSelectedListener(this);
}
注意popup的dismiss監聽,在每次dismiss時都會給外部發選擇完成的地址(isLast決定是否傳送)。
- 網路獲取資料用AraeData接收,類中有2個欄位,地址id和地址名稱
- 使用者選擇地址的結果集存在AraeDataResult裡面,相比地址集多了一個position欄位,用來存放使用者選中的item的position,方便把選中的item移動到檢視頂部。
最主要的,還是2個監聽:
recycleview的item點選監聽
tablayout切換監聽(只用到3個方法中的onTabSelected)
//recycleivew的item點選監聽,也就是使用者選擇地址了
RvAdapter.OnSelectorListener mListener = new RvAdapter.OnSelectorListener() {
@Override
public void setSelect(String name, String id, int sPosition) {
int tabCount = mTabLayout.getTabCount();
//重新點選新地址後 迴圈刪除 舊tab
if (mTabCurrent < tabCount - 1) {
for (int i = 0; i < tabCount - 1 - mTabCurrent; i++) {
mTabLayout.removeTabAt(tabCount - 1 - i);
mResultList.remove(tabCount - 1 - i);
}
}
mTabLayout.getTabAt(mTabCurrent).setText(name);
mResultList.add(new AraeDataResult(id, name, sPosition));
itemClick = true;
getdata(id, "0", 0, false);
}
};
//tablayout中某個tab選中的監聽
@Override
public void onTabSelected(TabLayout.Tab tab) {
mTabCurrent = tab.getPosition();
if (itemClick) {
} else {
String nowID = "0";
int sPosition = 0;
int tabCount = mTabLayout.getTabCount();
//如果是最後一個tab 則不需要預設選中 並且移動recycle位置
if (mTabCurrent >= tabCount - 1) {
nowID = "0";
sPosition = 0;
} else {
nowID = mResultList.get(mTabCurrent + 1).getAreaId();
sPosition = mResultList.get(mTabCurrent + 1).getPosition();
}
getdata(mResultList.get(mTabCurrent).getAreaId(), nowID, sPosition, true);
}
}
//新增Tab
private void addTab() {
mTabLayout.addTab(mTabLayout.newTab().setText("請選擇"), true);
}
/**
* 網路獲取資料
*
* @param pId 每級地址ID
* @param nowId 當前選中的地址id 在recycleview的選中效果中使用
* @param sposition 當前選中的item的position 用來把選中的item移動到第一位置
* @param onlyShow 用來判斷點選的為item 還是tab 如果是item 則要新增tab
*/
private void getdata(final String pId, final String nowId, final int sposition, final boolean onlyShow) {
isLast = false;
HttpManager manager = new HttpManager.Builder();
manager.setListener(new HttpManagerListen() {
@Override
public void onSucceed(String request) {
mAraeDatas = GsonManager.getInstance().getGson().fromJson(request, new TypeToken<List<AraeData>>() {
}.getType());
if (mAraeDatas == null || mAraeDatas.size() == 0) {
//沒有資料了 改變標示
isLast = true;
itemClick = false;
}
//無資料標示成立,說明是最後一級
if (isLast) {
popupWindow.dismiss();
return;
}
//如果是點選item,則新增tab
if (!onlyShow) {
addTab();
}
RvAdapter adapter = new RvAdapter(mContext, nowId, mAraeDatas, mListener);
mRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
mRecyclerView.setAdapter(adapter);
//顯示item為檢視第一個
if (onlyShow) {
mRecyclerView.scrollToPosition(sposition);
}
itemClick = false;
}
全場的難點就在這2個監聽裡面,getdata()中是網路獲取資料onSucceed請求成功這部分程式碼。
- recycleivew的item點選監聽裡面有個for迴圈需要注意,這個主要針對使用者已經選了幾級地址的情況下,他要點選tab返回重新選,這個時候要刪除掉已經選擇的部分地址,從程式碼上來說就是remove掉部分tab,並且從選中地址的結果集mResultList中remove掉一部分。
- tablayout中tab選中監聽裡面有一堆的判斷,說明為什麼有這些判斷,因為使用者點選item和點選tab是不一樣的,相同點都是需要重新請求網路給recycleview繫結資料,不同點是如果點選的是item還另外需要新增tab,而我們新增tab中是預設選中新增的tab,這個時候又觸發了tab 的點選,用判斷把這2種點選事件的處理隔離開,每次僅僅處理一種點選。
private boolean itemClick;就是為此而生的:
1.如果確認使用者點選的是item,則在選中tab的監聽中不做操作。
2.如果確認使用者點選的是tab,則需要重新繫結tab那一級的資料給使用者看,因為這一級資料是使用者選中過的,我們還要通過nowID 和sPosition 欄位,為使用者在列表中標記並置頂選中的item。標記就是那個黃色對號,在recycleview的adapter中做出效果,置頂在getdata()方法中的最後scrollToPosition()。 - getdata()中,如果接收到的資料為空,那麼說明已經到了最後一級,就呼叫popupwin.dismiss關掉popup,而dimiss的監聽中會把選擇地址結果集mResultList 發出去。
總結
總體實現不難,需要注意的地方有:
- recycleview單選實現以及item移動到頂部
- tablayout子tab的管理
- 選擇結果集List<AraeDataResult> mResultList的維護
- item點選處理與tab點選處理的隔離
歡迎評論區提問
點個讚唄
對於生活理想,應該像宗教徒對待宗教一樣充滿虔誠與熱情!
相關文章
- 模仿京東選擇地址器
- Android小記-仿淘寶聯動地址選擇對話方塊Android
- 自定義一個仿拼多多地址選擇器
- 微信小程式三級聯動地址選擇器微信小程式
- 仿百度文庫文件上傳頁面的多級聯動分類選擇器
- mui仿京東地址 css + js (動態資料)UICSSJS
- CSS多類選擇器CSS
- 仿網易新聞頭部多按鈕選擇器-SegmentControl-SwiftSwift
- CSS父級選擇器 :has()CSS
- 一次地址選擇器的實踐
- 基於vue2實現多級聯動選擇器Vue
- 仿淘寶、京東拖拽商品詳情(可巢狀ViewPager、ListView、WebView、FragmentTabhost)巢狀ViewpagerWebViewFragment
- iOS仿滴滴預約用車時間選擇器iOS
- 002---選擇器(標籤選擇器、類選擇器、id選擇器、偽類選擇器、萬用字元選擇器)字元
- 『為移動端而生』的自定義多級聯動選擇器
- jQuery選擇器介紹:基本選擇器、層次選擇器、過濾選擇器、表單選擇器jQuery
- 【Flutter 專題】97 仿網易新聞標籤選擇器Flutter
- 仿淘寶物流介面
- jQuery選擇器——基本選擇器jQuery
- 仿微信iOS相簿選擇 MTImagePickeriOS
- 微信小程式--手寫一個地區選擇器(多級聯動)微信小程式
- Material之Behavior實現支付寶密碼彈窗 仿淘寶/天貓商品屬性選擇密碼
- 基本CSS選擇器,複合選擇器,後代選擇器CSS
- jQuery選擇器——層次選擇器jQuery
- Android 仿微信的圖片選擇器ImageSelector的使用Android
- Android 實現一個仿微信的圖片選擇器Android
- JS仿淘寶詳情頁選單條智慧定位效果JS
- JQuery選擇器——可見性篩選選擇器和屬性篩選選擇器jQuery
- CSS3新增選擇器(屬性選擇器、結構偽類選擇器、偽元素選擇器)CSSS3
- Android專案總結(二)仿IOS效果的日期選擇器和省市縣三級聯動AndroidiOS
- jQuery選擇器之層次選擇器jQuery
- CSS選擇器(5)——屬性選擇器CSS
- jQuery選擇器——基本過濾選擇器jQuery
- CSS ID選擇器與CLASS選擇器CSS
- CSS-選擇器6-兄弟選擇器CSS
- jQuery選擇器——內容過濾選擇器jQuery
- jQuery選擇器——子元素過濾選擇器jQuery
- jQuery選擇器——屬性過濾選擇器jQuery