版权声明:本文为HaiyuKing原创文章,转载请注明出处!
前言
使用RecyclerView+ViewPager实现类似TabLayout+ViewPager效果。
效果图
使用步骤
一、项目组织结构图
注意事项:
1、 导入类文件后需要change包名以及重新import R文件路径
2、 Values目录下的文件(strings.xml、dimens.xml、colors.xml等),如果项目中存在,则复制里面的内容,不要整个覆盖
二、导入步骤
(1)在build.gradle中引用recyclerview【版本号和appcompat保持一致】
apply plugin: 'com.android.application' android {
compileSdkVersion 27
defaultConfig {
applicationId "com.why.project.viewpagerwithrecyclerdemo"
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
} dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' //RecyclerView
compile "com.android.support:recyclerview-v7:27.1.1"
}
(2)在项目中实现Recyclerview基本数据展现【顶部选项卡区域】
1、创建Bean类
package com.why.project.viewpagerwithrecyclerdemo.bean; /**
* Created by HaiyuKing
* Used
*/ public class TabItemBean {
private String tabTitle;
private String tabUrl; public TabItemBean(String tabTitle, String tabUrl){
this.tabTitle = tabTitle;
this.tabUrl = tabUrl;
} public String getTabTitle() {
return tabTitle;
} public void setTabTitle(String tabTitle) {
this.tabTitle = tabTitle;
} public String getTabUrl() {
return tabUrl;
} public void setTabUrl(String tabUrl) {
this.tabUrl = tabUrl;
}
}
TabItemBean.java
2、创建Adapter以及item的布局文件,同时定义颜色、文字大小值
package com.why.project.viewpagerwithrecyclerdemo.adapter; import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView; import com.why.project.viewpagerwithrecyclerdemo.R;
import com.why.project.viewpagerwithrecyclerdemo.bean.TabItemBean; import java.util.ArrayList; /**
* Created by HaiyuKing
* Used
*/ public class TabAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { /**上下文*/
private Context myContext;
/**集合*/
private ArrayList<TabItemBean> listitemList; private int selectedPosition = -1;//选中的列表项 /**
* 构造函数
*/
public TabAdapter(Context context, ArrayList<TabItemBean> itemlist) {
myContext = context;
listitemList = itemlist;
} /**
* 获取总的条目数
*/
@Override
public int getItemCount() {
return listitemList.size();
} /**
* 创建ViewHolder
*/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(myContext).inflate(R.layout.tab_list_item, parent, false);
ItemViewHolder itemViewHolder = new ItemViewHolder(view);
return itemViewHolder;
} /**
* 声明grid列表项ViewHolder
*/
static class ItemViewHolder extends RecyclerView.ViewHolder {
public ItemViewHolder(View view) {
super(view); listItemLayout = (RelativeLayout) view.findViewById(R.id.listitem_layout);
mTabTitle = (TextView) view.findViewById(R.id.top_title);
mTabUnderLine = (TextView) view.findViewById(R.id.top_underline);
} RelativeLayout listItemLayout;
TextView mTabTitle;
TextView mTabUnderLine;
} /**
* 将数据绑定至ViewHolder
*/
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int index) { //判断属于列表项
if (viewHolder instanceof ItemViewHolder) {
TabItemBean tabItemBean = listitemList.get(index);
final ItemViewHolder itemViewHold = ((ItemViewHolder) viewHolder); itemViewHold.mTabTitle.setText(tabItemBean.getTabTitle()); //设置下划线的宽度值==文本的宽度值
itemViewHold.mTabTitle.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
Log.w("why", "top_title.getMeasuredWidth()="+itemViewHold.mTabTitle.getMeasuredWidth());
itemViewHold.listItemLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
Log.w("why", "toptabLayout.getMeasuredWidth()="+itemViewHold.listItemLayout.getMeasuredWidth());
itemViewHold.mTabUnderLine.setWidth(itemViewHold.mTabTitle.getMeasuredWidth());//手动设置下划线的宽度值 if(selectedPosition == index){
itemViewHold.mTabTitle.setTextColor(myContext.getResources().getColor(R.color.tab_text_selected_top));
itemViewHold.mTabUnderLine.setBackgroundColor(myContext.getResources().getColor(R.color.tab_auto_selected_top));
}else{
itemViewHold.mTabTitle.setTextColor(myContext.getResources().getColor(R.color.tab_text_normal_top));
itemViewHold.mTabUnderLine.setBackgroundColor(myContext.getResources().getColor(R.color.tab_auto_normal_top));
} //如果设置了回调,则设置点击事件
if (mOnItemClickLitener != null) {
itemViewHold.listItemLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = itemViewHold.getLayoutPosition();//在增加数据或者减少数据时候,position和index就不一样了
mOnItemClickLitener.onItemClick(itemViewHold.listItemLayout, position);
setSelected(position);
}
});
} }
} /**更改选中的列表项下标值*/
public void setSelected(int selected) {
this.selectedPosition = selected;
notifyDataSetChanged();
} /**
* 添加Item--用于动画的展现
*/
public void addItem(int position, TabItemBean listitemBean) {
listitemList.add(position, listitemBean);
notifyItemInserted(position);
} /**
* 删除Item--用于动画的展现
*/
public void removeItem(int position) {
listitemList.remove(position);
notifyItemRemoved(position);
} /*=====================添加OnItemClickListener回调================================*/
public interface OnItemClickLitener {
void onItemClick(View view, int position);
} private OnItemClickLitener mOnItemClickLitener; public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener) {
this.mOnItemClickLitener = mOnItemClickLitener;
}
}
<?xml version="1.0" encoding="utf-8"?>
<!-- 带有下划线的顶部选项卡子项的布局文件-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/listitem_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="@dimen/tab_top_auto_padding"
android:paddingLeft="@dimen/tab_top_auto_padding"
android:paddingRight="@dimen/tab_top_auto_padding"
>
<!-- 标题 -->
<TextView
android:id="@+id/top_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text=""
android:textSize="@dimen/tab_top_auto_title_size"
android:textColor="@color/tab_text_normal_top"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />
<!-- 下划线-->
<!-- android:background="@color/tab_underline_selected_top" -->
<TextView
android:id="@+id/top_underline"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_top_auto_height"
android:background="@color/tab_auto_normal_top"
android:layout_below="@id/top_title"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/tab_top_auto_padding"
/> </RelativeLayout>
tab_list_item.xml
在colors.xml文件中添加以下代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color> <!-- *********************************顶部选项卡区域********************************* -->
<!-- 顶部选项卡下划线背景色 -->
<color name="tab_auto_normal_top">#00ffffff</color>
<color name="tab_auto_selected_top">#3090d9</color>
<!-- 顶部选项卡文本颜色 -->
<color name="tab_text_normal_top">#191919</color>
<color name="tab_text_selected_top">#3090d9</color>
</resources>
在dimens.xml文件中添加以下代码
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen> <!-- *********************************顶部选项卡区域********************************* -->
<!-- 选项卡的内边距 -->
<dimen name="tab_top_auto_padding">10dp</dimen>
<!-- 选项卡标题的文字大小 -->
<dimen name="tab_top_auto_title_size">18sp</dimen>
<!-- 选项卡标题的下划线高度 -->
<dimen name="tab_top_auto_height">3dp</dimen>
</resources>
3、在Activity布局文件中引用Recyclerview控件【见下文】
4、在Activity类中初始化recyclerview数据【见下文】
(3)创建需要用到的fragment类和布局文件
(4)引入MyCustomViewPager并参考MyCustomViewPagerAdapter创建viewpager的适配器
package com.why.project.viewpagerwithrecyclerdemo.adapter; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.view.ViewGroup; import java.util.List; /**
* Created by HaiyuKing
* Used
*/ public class MyTabViewPagerAdapter extends FragmentStatePagerAdapter { /**碎片集合*/
private List<Fragment> fragmentList; public MyTabViewPagerAdapter(FragmentManager fm) {
super(fm);
// TODO Auto-generated constructor stub
} /**
* 自定义构造函数:用于传递碎片集合过来
* 一般都写上*/
public MyTabViewPagerAdapter(FragmentManager fm,List<Fragment> fragmentList) {
super(fm);
this.fragmentList = fragmentList;
} @Override
public Fragment getItem(int arg0) {
// TODO Auto-generated method stub
return fragmentList.get(arg0);
} @Override
public int getCount() {
return fragmentList.size();
} @Override
public void destroyItem(ViewGroup container, int position, Object object) {
//viewpager+fragment来回滑动fragment重新加载的简单解决办法:http://blog.csdn.net/qq_28058443/article/details/51519663
//建议保留,因为首页碎片界面含有6个界面切换,总不能设置setOffscreenPageLimit(6)吧,而且设置个数少的话,销毁后还得重新加载
//super.destroyItem(container, position, object);
}
}
MyTabViewPagerAdapter.java
(5)因为Demo中使用到了webview请求网址,所以需要在AndroidManifest.xml中添加权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.why.project.viewpagerwithrecyclerdemo"> <!-- ======================授权访问网络(HttpUtil)========================== -->
<!-- 允许程序打开网络套接字 -->
<uses-permission android:name="android.permission.INTERNET"/> <application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application> </manifest>
三、使用方法
(1)在布局文件中引入RecyclerView和MyCustomViewPager【注意,修改MyCustomViewPage的完整路径】
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"> <!-- 横向列表 -->
<android.support.v7.widget.RecyclerView
android:id="@+id/tab_top_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content" /> <!-- 横分割线 -->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#e9e9e9" /> <!-- viewpager区域,配合Fragment使用,且不能滑动 -->
<com.why.project.viewpagerwithrecyclerdemo.viewpager.MyCustomViewPager
android:id="@+id/vp_tab"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" /> </LinearLayout>
(2)在Activity中填充数据,关联RecyclerView和ViewPager
package com.why.project.viewpagerwithrecyclerdemo; import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View; import com.why.project.viewpagerwithrecyclerdemo.adapter.MyTabViewPagerAdapter;
import com.why.project.viewpagerwithrecyclerdemo.adapter.TabAdapter;
import com.why.project.viewpagerwithrecyclerdemo.bean.TabItemBean;
import com.why.project.viewpagerwithrecyclerdemo.fragment.WebViewFragment;
import com.why.project.viewpagerwithrecyclerdemo.viewpager.MyCustomViewPager; import java.util.ArrayList;
import java.util.List; public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName(); //分类列表
private RecyclerView mTabRecycleView;
private ArrayList<TabItemBean> mTabItemBeanArrayList;
private TabAdapter mTabAdapter; /**保存的选项卡的下标值*/
private int savdCheckedIndex = 0;
/**当前的选项卡的下标值*/
private int mCurrentIndex = -1; //横向滚动的ViewPager
private MyCustomViewPager mViewPager;
/**滑屏适配器*/
private MyTabViewPagerAdapter viewPagerAdapter;
/**碎片集合*/
private List<Fragment> fragmentList;
//碎片界面
private WebViewFragment mWebViewFragment; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //初始化控件以及设置
initView();
//初始化数据
initData();
//初始化控件的点击事件
initEvent();
} @Override
protected void onResume() {
super.onResume();
Log.e(TAG,"{onResume}savdCheckedIndex="+savdCheckedIndex);
//设置保存的或者初始的选项卡标红显示
setTabsDisplay(savdCheckedIndex); mCurrentIndex = -1;//解决按home键后长时间不用,再次打开显示空白的问题
//设置保存的或者初始的选项卡展现对应的fragment
setFragmentDisplay(savdCheckedIndex);
} /**
* 初始化控件
*/
private void initView() {
mTabRecycleView = findViewById(R.id.tab_top_recycler);
mViewPager = (MyCustomViewPager) findViewById(R.id.vp_tab);
}
/**
* 初始化数据
*/
public void initData() {
//初始化集合
mTabItemBeanArrayList = new ArrayList<TabItemBean>(); mTabItemBeanArrayList.add(new TabItemBean("百度","http://www.baidu.com"));
mTabItemBeanArrayList.add(new TabItemBean("CSDN","http://blog.csdn.net"));
mTabItemBeanArrayList.add(new TabItemBean("博客园","http://www.cnblogs.com"));
mTabItemBeanArrayList.add(new TabItemBean("极客头条","http://geek.csdn.net/mobile"));
mTabItemBeanArrayList.add(new TabItemBean("优设","http://www.uisdc.com/"));
mTabItemBeanArrayList.add(new TabItemBean("玩Android","http://www.wanandroid.com/index"));
mTabItemBeanArrayList.add(new TabItemBean("掘金","https://juejin.im/")); //设置布局管理器【表情分类列表】
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);
mTabRecycleView.setLayoutManager(linearLayoutManager);
//设置适配器
if(mTabAdapter == null){
//设置适配器
mTabAdapter = new TabAdapter(this, mTabItemBeanArrayList);
mTabRecycleView.setAdapter(mTabAdapter);
//添加分割线
//设置添加删除动画
//调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局
mTabRecycleView.setSelected(true);
}else{
mTabAdapter.notifyDataSetChanged();
} //将碎片添加到集合中
fragmentList = new ArrayList<>();
for(int i=0;i<mTabItemBeanArrayList.size();i++){
TabItemBean tabItemBean = mTabItemBeanArrayList.get(i); Bundle bundle = new Bundle();
bundle.putString("param", tabItemBean.getTabUrl());
fragmentList.add(WebViewFragment.getInstance(WebViewFragment.class,bundle));
}
//给ViewPager设置适配器
viewPagerAdapter = new MyTabViewPagerAdapter(getSupportFragmentManager(),fragmentList);
mViewPager.setAdapter(viewPagerAdapter);
} /**
* 初始化点击事件
* */
private void initEvent() {
//列表适配器的点击监听事件
mTabAdapter.setOnItemClickLitener(new TabAdapter.OnItemClickLitener() {
@Override
public void onItemClick(View view, int position) {
if (mCurrentIndex == position) {
return;
}
setFragmentDisplay(position);//独立出来,用于OnResume的时候初始化展现相应的Fragment savdCheckedIndex = position;
mCurrentIndex = position;
}
}); //viewpager的滑动事件监听
mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
/*此方法是页面跳转完后得到调用,arg0是你当前选中的页面的Position(位置编号)。*/
public void onPageSelected(int arg0) {
//解决滑动后,无法点击上一个fragment选项卡的问题
setTabsDisplay(arg0);
savdCheckedIndex = arg0;
mCurrentIndex = arg0;
} @Override
/*
* 当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到调用。其中三个参数的含义分别为:
* arg0 :当前页面,及你点击滑动的页面
* arg1:当前页面偏移的百分比
* arg2:当前页面偏移的像素位置 */
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub } @Override
/*此方法是在状态改变的时候调用,其中arg0这个参数有三种状态(0,1,2)。
* arg0==0的时候默示什么都没做
* arg0 ==1的时候默示正在滑动
* arg0==2的时候默示滑动完毕了*/
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub }
});
} /**
* 设置导航栏中选项卡之间的切换
*/
private void setTabsDisplay(int index){
if(mTabAdapter != null){
mTabAdapter.setSelected(index);
mTabRecycleView.smoothScrollToPosition(index);//平移滑动到指定position
}
} /**
* 设置碎片之间的切换
*/
private void setFragmentDisplay(int index){
mViewPager.setCurrentItem(index, false);//smoothScroll false表示切换的时候,不经过两个页面的中间页
} /**
* http://blog.csdn.net/caesardadi/article/details/20382815
* */
// 自己记录fragment的位置,防止activity被系统回收时,fragment错乱的问题【按home键返回到桌面一段时间,然后在进程里面重新打开,会发现RadioButton的图片选中状态在第二个,但是文字和背景颜色的选中状态在第一个】
//onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。
protected void onSaveInstanceState(Bundle outState) {
//http://www.cnblogs.com/chuanstone/p/4672096.html?utm_source=tuicool&utm_medium=referral
//总是执行这句代码来调用父类去保存视图层的状态”。其实到这里大家也就明白了,就是因为这句话导致了重影的出现
super.onSaveInstanceState(outState);
outState.putInt("selectedCheckedIndex", savdCheckedIndex);
outState.putInt("mCurrentIndex", mCurrentIndex);
} @Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
savdCheckedIndex = savedInstanceState.getInt("selectedCheckedIndex");
mCurrentIndex = savedInstanceState.getInt("mCurrentIndex");
super.onRestoreInstanceState(savedInstanceState);
} }
混淆配置
无
参考资料
暂时空缺