亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android模仿實(shí)現(xiàn)閑魚首頁的思路與方法

 更新時(shí)間:2017年04月23日 16:08:06   作者:Conan_Lee  
這篇文章主要給大家介紹了Android模仿實(shí)現(xiàn)閑魚首頁的思路與方法,文中通過示例代碼介紹的非常詳細(xì),并在文末給出了完整的代碼供大家參考學(xué)習(xí),需要的朋友們下面來一起看看吧。

首先我們來看看效果圖


Demo是基于MVVM模式來編寫的,歡迎大家給予批評和指正。

其中Banner的無限輪播用了PageSnapHelper,后續(xù)RecycleView也可以實(shí)現(xiàn)更多類似ViewPage的效果了

可以看出頁面大概可以分為這幾個(gè)部分

      1.最上面是一個(gè)輪播的Banner

      2.中間可能有些其他的功能列表

      3.最后是Tab頁(這里是新鮮的和附近的兩個(gè)列表)

OK,看到這樣的布局需求的時(shí)候可能有兩種思路

      1、整體是一個(gè)RefreshLayout布局,內(nèi)嵌RecycleView,而Banner頁,其他功能列表以及TabLayout都當(dāng)成RecycleView的頭加入到RecycleView中,TabLayout下面是真正的列表項(xiàng)

      2、整體還是一個(gè)RefreshLayout布局,內(nèi)部是一個(gè)NestScrollView,Banner頁,其他功能列表,TabLayout依次布局在NestScrollView中,然后最下面布局一個(gè)FrameLayout,TabLayout切換的時(shí)候切換不同的Fragment

Demo中使用的是第一種方式,第二種方式考慮到SwipeRefreshLayout和內(nèi)部FrameLayout的滑動會有沖突,后續(xù)再嘗試編寫

接下來考慮需要考慮的問題

  1. TabLayout需要固定到頂部
  2. 第一次加載數(shù)據(jù)的時(shí)候需要有個(gè)Loading提示,Demo中就是一個(gè)小魚的空白等待頁
  3. 因?yàn)槭褂靡粋€(gè)數(shù)據(jù)集,在TabLayout來回切換的時(shí)候需要保證數(shù)據(jù)集合所在的位置是正確的(比如新鮮的這個(gè)列表當(dāng)前在Position1的位置,切換到附近的列表我滑到了Position2的位置,當(dāng)我再切回新鮮的時(shí)候需要回到Position1的位置)

下面就一些核心的代碼和思路講解一下

首先是布局,布局很簡單,SwipeRefreshLayout中包了一個(gè)FrameLayout,然后在FrameLayout中包含了一個(gè)RecycleView

<android.support.v4.widget.SwipeRefreshLayout
 android:id="@+id/layout_refresh"
 android:layout_width="match_parent"
 android:layout_height="match_parent">

 <FrameLayout
  android:id="@+id/container"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <android.support.v7.widget.RecyclerView
  android:id="@+id/list"
  android:layout_width="match_parent"
  android:layout_height="match_parent" />
 </FrameLayout>
 </android.support.v4.widget.SwipeRefreshLayout>

接下來看下StickyHead如何實(shí)現(xiàn)

//正常的TabLayout布局
private TabLayout mTabLayout;
//粘性 TabLayout布局(用于固定在頂部)
private TabLayout mStickyTabLayout;
//粘性布局的Y坐標(biāo)(用戶判斷粘性布局是否顯示)
private int mStickyPositionY;
//主列表布局
private RecyclerView mHomeList;

這是變量的定義,下面的這個(gè)類是我將一些頁面邏輯涉及的變量抽離出來

public class HomeEntity extends BaseObservable {

//列表類型 0:新鮮的 1:附近的
public static final int LIST_TYPE_FRESH = 0;
public static final int LIST_TYPE_NEAR = 1;

private int bannerCount;
private int listType = LIST_TYPE_FRESH;
//新鮮的和附近的首次加載的loading狀態(tài)
private boolean refreshLoading;
private boolean nearLoading;
//首頁是否正在下拉刷新
private boolean refreshing;
//新鮮的和附近的 獲取更多的View的狀態(tài)值(用戶記錄TabLayout切換的時(shí)候,LoadingMore的狀態(tài))
private int refreshMoreStatus;
private int nearMoreStatus;
//首頁的活動更多的狀態(tài)
private int loadingMoreStatus;
}

這是變量的定義,然后初始化兩個(gè)TabLayout,主要在于需要監(jiān)聽TabLayout的切換

mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
 @Override
 public void onTabSelected(TabLayout.Tab tab) {
  int position = tab.getPosition();
  //設(shè)置粘貼TabLayout的選中Tab
  if (!mStickyTabLayout.getTabAt(position).isSelected()) {
  mStickyTabLayout.getTabAt(position).select();
  mViewModel.changeHomeData(position);
  }
 }

 @Override
 public void onTabUnselected(TabLayout.Tab tab) {

 }

 @Override
 public void onTabReselected(TabLayout.Tab tab) {

 }
 });
mStickyTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
 @Override
 public void onTabSelected(TabLayout.Tab tab) {
  int position = tab.getPosition();
  if (!mTabLayout.getTabAt(position).isSelected()) {
  mTabLayout.getTabAt(position).select();
  mHomeList.stopScroll();

  //mAdapter.setEnableLoadMore(false);
  mViewModel.changeHomeData(position);
  ......
  }
 }

 @Override
 public void onTabUnselected(TabLayout.Tab tab) {

 }

 @Override
 public void onTabReselected(TabLayout.Tab tab) {

 }
 });

這段的邏輯比較簡單,就是實(shí)現(xiàn)了保持TabLayout切換狀態(tài)的統(tǒng)一,當(dāng)TabLayout切換的時(shí)候,需要將StickyTabLayout所選中的Tab也設(shè)置一下,mViewModel.changeHomeData(position)這句話是為了切換數(shù)據(jù),下面會分析到

接下來是StickyHead重要的代碼

mHomeList.addOnScrollListener(new RecyclerView.OnScrollListener() {
 @Override
 public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
  int[] location = new int[2];
  mTabLayout.getLocationInWindow(location);
  int count = mViewContainer.getChildCount();
  if (location[1] <= mStickyPositionY) {
  if (count == 1) {
   mViewContainer.addView(mStickyTabLayout);
   mBinding.layoutRefresh.setEnabled(false);
  }
  } else {
  if (count > 1) {
   mViewContainer.removeView(mStickyTabLayout);
   //mOffsetY = DisplayUtil.dip2px(mContainer.getContext(), 46);
   //mRefreshPosition = mAdapter.getHeaderLayoutCount();
   //mNearPosition = mAdapter.getHeaderLayoutCount();
   mBinding.layoutRefresh.setEnabled(true);
  }
  }

  //if (mInitPositionY == -1) {
  //mInitPositionY = location[1];
  //}
  //mHomeListPositionY = location[1];
 }
 });

主要邏輯就是先獲取TabLayout在窗口的位置,如果Y坐標(biāo)小于粘貼頭部的Y坐標(biāo),則將粘貼頭部加入到布局中來并顯示,否則,將粘貼頭部布局從布局中移除。判斷count這個(gè)值是為了防止重復(fù)添加和重復(fù)移除粘貼頭布局。

mBinding.layoutRefresh.setEnabled(true/false)是為了在粘貼頭部固定在頂上的時(shí)候消除掉外層SwipeRefreshLayout的下拉刷新錯(cuò)誤。注釋掉的代碼會在下面再講

只需要上面的這么多代碼一個(gè)StickyHead就實(shí)現(xiàn)了,在測試的時(shí)候遇到點(diǎn)小問題,就是焦點(diǎn)重置導(dǎo)致的RecycleView重新回到初始位置的一個(gè)錯(cuò)誤,下面是暫時(shí)的解決方案

LinearLayoutManager manager = new LinearLayoutManager(mContainer.getContext()) {
 @Override
 public boolean onRequestChildFocus(RecyclerView parent, RecyclerView.State state, View child, View focused) {
  //TODO 暫時(shí)處理View焦點(diǎn)問題
  return true;
 }
 };

下面簡單說下如何實(shí)現(xiàn)首次加載新鮮的或者附近的數(shù)據(jù)的時(shí)候出現(xiàn)的一個(gè)等待頁面

主要思路是這樣的

      1、這個(gè)等待的LoadingView是當(dāng)成RecycleView的頭加在TabLayout后面的,當(dāng)數(shù)據(jù)加載完成這個(gè)LoadingView設(shè)置為不可見

      2、因?yàn)橛蠺abLayout會切換,導(dǎo)致RecycleView的數(shù)據(jù)會重新繪制,進(jìn)而導(dǎo)致RecyView會回到初始位置,所以需要記錄下RecycleView所在的位置,然后手動滑動到記錄的位置

具體的我們還是來看代碼吧

private int mHomeListPositionY;//用來標(biāo)識當(dāng)前RecycleView的位置
private int mInitPositionY = -1;//初始狀態(tài)下RecycleView的Y坐標(biāo)
//這里是RecycleView的滑動監(jiān)聽,用來記錄RecycleView的位置,這里其實(shí)是記錄了mTabLayout的位置
mHomeList.addOnScrollListener(new RecyclerView.OnScrollListener() {
 @Override
 public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
  int[] location = new int[2];
  mTabLayout.getLocationInWindow(location);
  int count = mViewContainer.getChildCount();

  if (mInitPositionY == -1) {
  mInitPositionY = location[1];
  }
  mHomeListPositionY = location[1];
 }
 });
//這個(gè)函數(shù)就是用來手動將RecycleView滑動到正確的位置
 private void setLoadingView(boolean visible, int type) {
 int position;
 if (type == HomeEntity.LIST_TYPE_FRESH) {
 position = mRefreshPosition;
 } else {
 position = mNearPosition;
 }
 if (visible) {
 mLoadingView.setVisibility(View.VISIBLE);
 if (mStickyTabLayout.getVisibility() == View.VISIBLE) {
  LinearLayoutManager layoutManager = (LinearLayoutManager) mHomeList.getLayoutManager();
  layoutManager.scrollToPositionWithOffset(0, mHomeListPositionY - mInitPositionY);
 }
 } else {
 mLoadingView.setVisibility(View.GONE);
 LinearLayoutManager layoutManager = (LinearLayoutManager) mHomeList.getLayoutManager();
 if (mViewContainer.getChildCount() > 1) {
  layoutManager.scrollToPositionWithOffset(position, mStickyTabLayout.getHeight());
 } else {
  layoutManager.scrollToPositionWithOffset(0, mHomeListPositionY - mInitPositionY);
 }
 }
}

最后來看下新鮮的和附近的加載更多時(shí)頁面的實(shí)現(xiàn)

這里Adapter使用了第三方BRVAH,所以相對LoadingMore的狀態(tài)BRVAH幫我封了一下,因?yàn)殡m然是一個(gè)List,但其實(shí)是兩個(gè)列表復(fù)用一個(gè)List的,所以這里的LoadingMore狀態(tài)我們需要記錄兩個(gè),方便切換的時(shí)候列表的LoadingMore狀態(tài)是正確的,下面看下主要代碼

if (propertyId == BR.refreshLoading) {
  if (HomeEntity.LIST_TYPE_FRESH != entity.getListType()) {
  return;
  }
  if (mLoadingView.getVisibility() == View.GONE) {
  mAdapter.setEnableLoadMore(true);
  }
 } else if (propertyId == BR.nearLoading) {
  if (HomeEntity.LIST_TYPE_NEAR != entity.getListType()) {
  return;
  }
  if (mLoadingView.getVisibility() == View.GONE) {
  mAdapter.setEnableLoadMore(true);
  }
 } else if (propertyId == BR.loadingMoreStatus) {
  int status = entity.getLoadingMoreStatus();
  mAdapter.setEnableLoadMore(true);
  if (LoadMoreView.STATUS_DEFAULT == status) {
  mAdapter.loadMoreComplete();
  } else if (LoadMoreView.STATUS_END == status) {
  mAdapter.loadMoreEnd();
  } else if (LoadMoreView.STATUS_FAIL == status) {
  mAdapter.loadMoreFail();
  }
 }

BR.refreshLoadingBR.nearLoading 都是監(jiān)聽首次加載,這里

if (mLoadingView.getVisibility() == View.GONE) {
mAdapter.setEnableLoadMore(true);
}

這個(gè)是為了防止首次加載顯示Loading頁面的時(shí)候又顯示了LoadingMore布局

BR.loadingMoreStatus這個(gè)就是監(jiān)聽LoadingMore的狀態(tài)來更新List的Adapter

其他的主要ViewModel代碼在HomeViewModel中。

主要的幾個(gè)點(diǎn)

  • 粘貼頭布局的邏輯
  • TabLayout切換導(dǎo)致數(shù)據(jù)集變化以及位置的變化
  • 加載更多的時(shí)候需要考慮TabLayout切換的問題

項(xiàng)目鏈接:https://github.com/ly85206559/demo4Fish

本地下載:點(diǎn)擊這里

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Android實(shí)現(xiàn)信息彈出框

    Android實(shí)現(xiàn)信息彈出框

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)信息彈出框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-04-04
  • Android自定義dialog 自下往上彈出的實(shí)例代碼

    Android自定義dialog 自下往上彈出的實(shí)例代碼

    本文通過實(shí)例代碼給大家介紹了Android自定義dialog 自下往上彈出效果,代碼簡單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2018-08-08
  • android利用handler實(shí)現(xiàn)打地鼠游戲

    android利用handler實(shí)現(xiàn)打地鼠游戲

    這篇文章主要為大家詳細(xì)介紹了android利用handler實(shí)現(xiàn)打地鼠游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • Android App中進(jìn)行語言的切換

    Android App中進(jìn)行語言的切換

    這篇文章主要介紹了Android App中如何進(jìn)行語言的切換,幫助大家更好的理解和學(xué)習(xí)使用Android app,感興趣的朋友可以了解下
    2021-03-03
  • Android創(chuàng)建一個(gè)Activity的方法分析

    Android創(chuàng)建一個(gè)Activity的方法分析

    這篇文章主要介紹了Android創(chuàng)建一個(gè)Activity的方法,結(jié)合實(shí)例形式分析了Android創(chuàng)建Activity的具體步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2016-04-04
  • 詳解Android應(yīng)用層制作LED指示燈

    詳解Android應(yīng)用層制作LED指示燈

    這篇文章主要介紹了詳解Android應(yīng)用層制作LED指示燈的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • Android中WebView的一些簡單用法

    Android中WebView的一些簡單用法

    這篇文章主要介紹了Android中WebView的一些簡單用法,WebView非常簡單,Android已經(jīng)封裝的非常完善,寫個(gè)小例子覆蓋其間常用的幾個(gè)方法;
    2016-08-08
  • Windows下搭建Android開發(fā)環(huán)境

    Windows下搭建Android開發(fā)環(huán)境

    這篇文章主要介紹了Windows下搭建Android開發(fā)環(huán)境,需要的朋友可以參考下
    2015-09-09
  • Android開發(fā)之DialogFragment用法實(shí)例總結(jié)

    Android開發(fā)之DialogFragment用法實(shí)例總結(jié)

    這篇文章主要介紹了Android開發(fā)之DialogFragment用法,結(jié)合實(shí)例形式總結(jié)分析了Android使用DialogFragment代替Dialog功能的相關(guān)使用技巧與注意事項(xiàng),需要的朋友可以參考下
    2017-11-11
  • Android 使用PopupWindow實(shí)現(xiàn)彈出更多的菜單實(shí)例詳解

    Android 使用PopupWindow實(shí)現(xiàn)彈出更多的菜單實(shí)例詳解

    最近想要做一個(gè)彈出更多的菜單,而原生的彈出菜單卻不是我們想要的效果,所以必然要自定義菜單。接下來通過本文給大家介紹android 使用popupwindow實(shí)現(xiàn)彈出更多的菜單實(shí)例詳解,需要的朋友可以參考下
    2017-04-04

最新評論