热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android多个TAB选项卡切换效果

这篇文章主要介绍了Android多个TAB选项卡切换效果的实现代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

在前一期中,我们做了悬浮头部的两个tab切换和下拉刷新效果,后来项目中要求改成三个tab,当时就能估量了一下,如果从之前的改,也不是不可以,但是要互相记住的状态就太多了,很容易出现错误。就决定重新实现一下这个效果,为此先写了一个demo,这期间项目都已经又更新了两个版本了。demo还木有变成文章。

之前的版本中是采用了一个可以下拉刷新的listview,之后在listview中添加了两个头部,并且在该布局上的上面用了一个一模一样的切换tab,如果没有看过前面版本的,可以看看前一个版本,Listview多Tab上滑悬浮。

基于上述思路我们先来看看页面布局:main_activity

<&#63;xml version="1.0" encoding="utf-8"&#63;>


 

 

 

 

 

 

 
 


页面采用了两层,后面一层为Viewpager,前面为悬浮头与tab切换,在这大家应该都想到了会怎么样实现,Viewpager中添加已经fragment,每个fragment里面加入一个可下拉刷新的Listview,根据ListView的滑动来控制前一帧页面的位置。

来看看页面代码吧,MainAcitivity.java

public class MainActivity extends ActionBarActivity implements OnPageChangeListener, ScrollTabHolder {

 private PagerSlidingTabStrip tabs;

 private ViewPager viewPager;

 private SlidingPagerAdapter adapter;

 private LinearLayout header;

 private int headerHeight;
 private int headerTranslationDis;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main_activity);
 getHeaderHeight();
 findViews();
 setupPager();
 setupTabs();
 }

 private void findViews() {
 tabs = (PagerSlidingTabStrip) findViewById(R.id.show_tabs);
 viewPager = (ViewPager) findViewById(R.id.pager);
 header = (LinearLayout) findViewById(R.id.header);
 }

 private void getHeaderHeight() {
 headerHeight = getResources().getDimensionPixelSize(R.dimen.max_header_height);
 headerTranslatiOnDis= -getResources().getDimensionPixelSize(R.dimen.header_offset_dis);
 }

 private void setupPager() {
 adapter = new SlidingPagerAdapter(getSupportFragmentManager(), this, viewPager);
 adapter.setTabHolderScrollingListener(this);//控制页面上滑
 viewPager.setOffscreenPageLimit(adapter.getCacheCount());
 viewPager.setAdapter(adapter);
 viewPager.setOnPageChangeListener(this);
 }

 private void setupTabs() {
 tabs.setShouldExpand(true);
 tabs.setIndicatorColorResource(R.color.color_purple_bd6aff);
 tabs.setUnderlineColorResource(R.color.color_purple_bd6aff);
 tabs.setCheckedTextColorResource(R.color.color_purple_bd6aff);
 tabs.setViewPager(viewPager);
 }

 @Override
 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
 tabs.onPageScrolled(position, positionOffset, positionOffsetPixels);
 }

 @Override
 public void onPageSelected(int position) {
 tabs.onPageSelected(position);
 reLocation = true;
 SparseArrayCompat scrollTabHolders = adapter.getScrollTabHolders();
 ScrollTabHolder currentHolder = scrollTabHolders.valueAt(position);
 if (NEED_RELAYOUT) {
  currentHolder.adjustScroll((int) (header.getHeight() + headerTop));// 修正滚出去的偏移量
 } else {
  currentHolder.adjustScroll((int) (header.getHeight() + ViewHelper.getTranslationY(header)));// 修正滚出去的偏移量
 }
 }

 @Override
 public void onPageScrollStateChanged(int state) {
 tabs.onPageScrollStateChanged(state);
 }

 @Override
 public void adjustScroll(int scrollHeight) {

 }

 private boolean reLocation = false;

 private int headerScrollSize = 0;

 public static final boolean NEED_RELAYOUT = Integer.valueOf(Build.VERSION.SDK).intValue() 
public class Tab1ListFragment extends ScrollTabHolderFragment {

 private PullToRefreshListView listView;

 private View placeHolderView;

 private ArrayAdapter adapter;

 private ArrayList listItems;

 private Handler handler;

 public Tab1ListFragment() {
 this.setFragmentId(PageAdapterTab.PAGE_TAB1.fragmentId);
 }

 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 }

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
 return inflater.inflate(R.layout.page_tab_fragment_layout, container, false);
 }

 @Override
 public void onActivityCreated(Bundle savedInstanceState) {
 super.onActivityCreated(savedInstanceState);
 findViews();
 initListView();
 }

 @SuppressLint("InflateParams")
 private void findViews() {
 handler = new Handler(Looper.getMainLooper());
 listView = (PullToRefreshListView) getView().findViewById(R.id.page_tab_listview);
 }

 private void initListView() {
 setListViewListener();
 listViewAddHeader();
 listViewLoadData();
 }

 private void setListViewListener() {
 listView.setOnRefreshListener(new OnRefreshListener2() {

  @Override
  public void onPullDownToRefresh(PullToRefreshBase refreshView) {
  loadNews();
  }

  @Override
  public void onPullUpToRefresh(PullToRefreshBase 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 (scrollTabHolder != null) {
   scrollTabHolder.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount, getFragmentId());
  }
  }
 });
 listView.setOnHeaderScrollListener(new OnHeaderScrollListener() {

  @Override
  public void onHeaderScroll(boolean isRefreashing, boolean istop, int value) {
  if (scrollTabHolder != null && istop) {
   scrollTabHolder.onHeaderScroll(isRefreashing, value, getFragmentId());
  }
  }
 });
 }

 private void listViewAddHeader() {
 placeHolderView = new LinearLayout(getActivity());
 AbsListView.LayoutParams params = new LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, getResources()
  .getDimensionPixelSize(R.dimen.max_header_height));
 placeHolderView.setLayoutParams(params);
 listView.getRefreshableView().addHeaderView(placeHolderView);
 }

 protected void listViewLoadData() {
 listItems = new ArrayList();
 for (int i = 1; i <= 50; i++) {
  listItems.add("currnet page: " + (getFragmentId() + 1) + " item --" + i);
 }
 adapter = new ArrayAdapter(getActivity(), R.layout.list_item, android.R.id.text1, listItems);
 listView.setAdapter(adapter);
 loadNews();
 }

 /**
 * 下拉清空旧的数据
 */
 private void loadNews() {
 handler.postDelayed(new Runnable() {// 模拟远程获取数据

   @Override
   public void run() {
   stopRefresh();
   // listItems.clear();
   // for (int i = 1; i <= 50; i++) {
   // listItems.add("currnet page: " + (getFragmentId() +
   // 1) + " item --" + i);
   // }
   // notifyAdpterdataChanged();
   }
  }, 300);
 }

 private void notifyAdpterdataChanged() {
 if (adapter != null) {
  adapter.notifyDataSetChanged();
 }
 }

 protected void loadOlds() {
 handler.postDelayed(new Runnable() {// 模拟远程获取数据

   @Override
   public void run() {
   stopRefresh();
   int size = listItems.size() + 1;
   for (int i = size; i = 2) {
  return;
 }
 //Log.d(getTag(), "scrollHeight:" + scrollHeight);
 listView.getRefreshableView().setSelectionFromTop(2, scrollHeight);
// Log.d(getTag(), "getScrollY:" + getScrollY(listView.getRefreshableView()));
// handler.postDelayed(new Runnable() {
//  
//  @Override
//  public void run() {
//  Log.d(getTag(), "getScrollY:" + getScrollY(listView.getRefreshableView()));  
//  }
// }, 5000);
 }

 public int getScrollY(AbsListView view) {
 View c = view.getChildAt(0);
 if (c == null) {
  return 0;
 }
 int top = c.getTop();
 int firstVisiblePosition = view.getFirstVisiblePosition();
 if (firstVisiblePosition == 0) {
  return -top;
 } else if (firstVisiblePosition == 1) {
  return top;
 } else {
  return -top + (firstVisiblePosition - 2) * c.getHeight() + 683;
 }
 }

 protected void updateListView() {
 if (adapter != null) {
  adapter.notifyDataSetChanged();
 }
 }

 protected void stopRefresh() {
 listView.onRefreshComplete();
 }

}

上面代码中的界面就是xml中包含了一个PullToRefreshListView,比较简单这个地方就不贴出来了,我们看到在listViewAddHeader中,这个地方添加了一个与悬浮头等高的头部,这样就可以将内容区域给呈现出来,不会被悬浮头遮挡,其次在list的listener中我们将onScorll传到了主界面,这样Listview滚动,就可以将当前滚动的距离计算出来,修正悬浮头的距离。

我们再贴出上面剩下的代码ScrollTabHolderFragment.java与ScrollTabHolder.java

public abstract class ScrollTabHolderFragment extends Fragment implements ScrollTabHolder {

 private int fragmentId;

 protected ScrollTabHolder scrollTabHolder;

 public void setScrollTabHolder(ScrollTabHolder scrollTabHolder) {
 this.scrollTabHolder = scrollTabHolder;
 }

 @Override
 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount,
  int pagePosition) {
 // nothing
 }

 @Override
 public void onHeaderScroll(boolean isRefreashing, int value, int pagePosition) {

 }

 public int getFragmentId() {
 return fragmentId;
 }

 public void setFragmentId(int fragmentId) {
 this.fragmentId = fragmentId;
 }
}


public interface ScrollTabHolder {

 void adjustScroll(int scrollHeight);

 void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount, int pagePosition);

 void onHeaderScroll(boolean isRefreashing, int value, int pagePosition);
}

最后我们来看看adaper

public class SlidingPagerAdapter extends FragmentPagerAdapter {

 protected final ScrollTabHolderFragment[] fragments;

 protected final Context context;

 private SparseArrayCompat mScrollTabHolders;
 private ScrollTabHolder mListener;

 public int getCacheCount() {
 return PageAdapterTab.values().length;
 }

 public SlidingPagerAdapter(FragmentManager fm, Context context, ViewPager pager) {
 super(fm);
 fragments = new ScrollTabHolderFragment[PageAdapterTab.values().length];
 this.cOntext= context;
 mScrollTabHolders = new SparseArrayCompat();
 init(fm);
 }

 private void init(FragmentManager fm) {
 for (PageAdapterTab tab : PageAdapterTab.values()) {
  try {
  ScrollTabHolderFragment fragment = null;

  List fs = fm.getFragments();
  if (fs != null) {
   for (Fragment f : fs) {
   if (f.getClass() == tab.clazz) {
    fragment = (ScrollTabHolderFragment) f;
    break;
   }
   }
  }

  if (fragment == null) {
   fragment = (ScrollTabHolderFragment) tab.clazz.newInstance();
  }

  fragments[tab.tabIndex] = fragment;
  } catch (InstantiationException e) {
  e.printStackTrace();
  } catch (IllegalAccessException e) {
  e.printStackTrace();
  }
 }
 }

 public void setTabHolderScrollingListener(ScrollTabHolder listener) {
 mListener = listener;
 }

 @Override
 public ScrollTabHolderFragment getItem(int pos) {
 ScrollTabHolderFragment fragment = fragments[pos];
 mScrollTabHolders.put(pos, fragment);
 if (mListener != null) {
  fragment.setScrollTabHolder(mListener);
 }
 return fragment;
 }

 public SparseArrayCompat getScrollTabHolders() {
 return mScrollTabHolders;
 }

 @Override
 public int getCount() {
 return PageAdapterTab.values().length;
 }

 @Override
 public CharSequence getPageTitle(int position) {
 PageAdapterTab tab = PageAdapterTab.fromTabIndex(position);
 int resId = tab != null &#63; tab.resId : 0;
 return resId != 0 &#63; context.getText(resId) : "";
 }

}

SlidingPagerAdapter 继承自FragmentPagerAdapter,从主界面传递了一个callback,将在callback传递给每个fragment,这样就将fragment与activity联系起来了。其实还有很多种方式,比如在fragment的attach中获取activity中的回调。上面代码中还有一个PageAdapterTab,它又是干什么的呐?来看看代码

public enum PageAdapterTab {
 PAGE_TAB1(0, Tab1ListFragment.class, R.string.page_tab1),

 PAGE_TAB2(1, Tab2ListFragment.class, R.string.page_tab2),

 PAGE_TAB3(2, Tab3ListFragment.class, R.string.page_tab3),
 ;

 public final int tabIndex;

 public final Class<&#63; extends Fragment> clazz;

 public final int resId;

 public final int fragmentId;

 private PageAdapterTab(int index, Class<&#63; extends Fragment> clazz, int resId) {
 this.tabIndex = index;
 this.clazz = clazz;
 this.resId = resId;
 this.fragmentId = index;
 }

 public static final PageAdapterTab fromTabIndex(int tabIndex) {
 for (PageAdapterTab value : PageAdapterTab.values()) {
  if (value.tabIndex == tabIndex) {
  return value;
  }
 }

 return null;
 }
}

就是一个枚举类,配置了当前要显示的fragment,这样以后就要增加就可以只修改改枚举就ok了

到此整个工程就结束了,我们截几张图看看效果:

最后在回顾一下,布局为两层,厚一层为一个Viewpager,里面包含了多个fragment,前一层为一个悬浮头与切换tab,当滑动listview时将当前显示的位置传递到主界面,同时更改主界面的位置。

代码地址如下:https://github.com/FreeSunny/RefreashTabView

以上就是本文的全部内容,希望对大家的学习有所帮助。


推荐阅读
  • RecyclerView初步学习(一)
    RecyclerView初步学习(一)ReCyclerView提供了一种插件式的编程模式,除了提供ViewHolder缓存模式,还可以自定义动画,分割符,布局样式,相比于传统的ListVi ... [详细]
  • Android LED 数字字体的应用与实现
    本文介绍了一种适用于 Android 应用的 LED 数字字体(digital font),并详细描述了其在 UI 设计中的应用场景及其实现方法。这种字体常用于视频、广告倒计时等场景,能够增强视觉效果。 ... [详细]
  • 本文基于对相关论文和开源代码的研究,详细介绍了LOAM(激光雷达里程计与建图)的工作原理,并对其关键技术进行了分析。 ... [详细]
  • Android 九宫格布局详解及实现:人人网应用示例
    本文深入探讨了人人网Android应用中独特的九宫格布局设计,解析其背后的GridView实现原理,并提供详细的代码示例。这种布局方式不仅美观大方,而且在现代Android应用中较为少见,值得开发者借鉴。 ... [详细]
  • 资源推荐 | TensorFlow官方中文教程助力英语非母语者学习
    来源:机器之心。本文详细介绍了TensorFlow官方提供的中文版教程和指南,帮助开发者更好地理解和应用这一强大的开源机器学习平台。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 构建基于BERT的中文NL2SQL模型:一个简明的基准
    本文探讨了将自然语言转换为SQL语句(NL2SQL)的任务,这是人工智能领域中一项非常实用的研究方向。文章介绍了笔者在公司举办的首届中文NL2SQL挑战赛中的实践,该比赛提供了金融和通用领域的表格数据,并标注了对应的自然语言与SQL语句对,旨在训练准确的NL2SQL模型。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文介绍如何使用 Sortable.js 库实现元素的拖拽和位置交换功能。Sortable.js 是一个轻量级、无依赖的 JavaScript 库,支持拖拽排序、动画效果和多种插件扩展。通过简单的配置和事件处理,可以轻松实现复杂的功能。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • 探讨一个显示数字的故障计算器,它支持两种操作:将当前数字乘以2或减去1。本文将详细介绍如何用最少的操作次数将初始值X转换为目标值Y。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 如何在WPS Office for Mac中调整Word文档的文字排列方向
    本文将详细介绍如何使用最新版WPS Office for Mac调整Word文档中的文字排列方向。通过这些步骤,用户可以轻松更改文本的水平或垂直排列方式,以满足不同的排版需求。 ... [详细]
  • 本文总结了在使用Ionic 5进行Android平台APK打包时遇到的问题,特别是针对QRScanner插件的改造。通过详细分析和提供具体的解决方法,帮助开发者顺利打包并优化应用性能。 ... [详细]
author-avatar
whdibk30
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有