1、需求分析
在饿了么首页中我们能看到这样的布局(如下图)。红框内是一个可以左右滑动的页面,每一个页面类似于九宫格,都有可供点击图标。对于这样的布局,我在网上找了很久都没有找到相关的名称,所以我这里暂时叫它导航页吧。
最近公司的项目就要求我实现一个这样的布局,但是我们的图标并不是想饿了么这样是固定的,所以在饿了么的布局上还要加一个效果:在图标数目无法排满两行时,就只显示一行。比如说,我们每一页最多可以显示两行和四列,当图标的总数目小于或等于4个时就只显示出一行,第二行就不要了。这样页面就不至于留出太多的空白。
先梳理一下我们要实现的效果:
大体的框架是一个可以左右滑动的页面;
每一页为两行四列的矩阵;
底部有和页面数目相等的导航点,且滑动到某个页面时,相应的导航点会改变颜色;
单选事件:选中图标后,图标变为完全不透明。
明白需求之后,我们穿越时空看看最后实现后的布局:
图片资源大家可以在我源码中找到。
2、实现思路
我们已经分析了需求,现在就来分析一下怎么实现。
必须事先说明一下,我的实现方法比较暴力,也比较占资源,所以大家要是有更好的方法的话欢迎留言告诉我,我们互相学习。
首先,这是一个左右滑动的页面,所以我们可以考虑使用ViewPager我们每个页面有8个图标,也就是8个item。在图标的数目在4以下时就只显示第一行,所以布局上我们可以将四个item作为一行放置到横向的线性布局中,当第二行没有图标时就让它消失。
ViewPager中的每一页的布局都是一样,所以我们可以对其复用:将图标分组,每8个为一组(余下的不到八页也归为一组),每一组即为一页。每次滑动的页面时就加载布局,填充图标和文字。
下面的导航点就比较简单了,可以使用ViewPager的滑动监听事件进行监听,滑动到选中的页面时就改变导航点的颜色。
至于透明,我们只需要记录图标的选中状态,然后改变透明度即可。
3、编写布局
接下来就让我们创建项目,编写页面布局吧。
在Android Studio中创建一个NavigationPager项目,MainActivity的布局如下:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@android:color/darker_gray"
tools:context="com.lindroid.navigationpager.MainActivity">
<com.lindroid.navigationpager.CustomViewPager
android:background="@android:color/white"
android:id="@+id/vp_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</com.lindroid.navigationpager.CustomViewPager>
<LinearLayout
android:background="@android:color/white"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:id="@+id/ll_dots"
android:orientation="horizontal"
android:layout_below="@+id/vp_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>
</LinearLayout>
MainActivity的主布局是一个纵向的线性布局,上面一个ViewPager,下面一个横向的线性布局,用于放置导航点。这里得说一下ViewPager的特性,在不指定特定高度的情况下,ViewPager的高度是默认填充整个父布局的,我们显然不希望这样,毕竟我们还要分一行和两行两种情况呢。所以这里我在这里使用了一个可以自适应高度的自定义ViewPager:
CustomViewPager
/**
* Created by Lindroid on 2017/3/20.
* 自适应高度的ViewPager
*/
public class CustomViewPager extends ViewPager {
public CustomViewPager(Context context) {
super(context);
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if (h > height)
height = h;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
页面的布局(也就是ViewPager的子布局)可以拆分成两个横向排列的布局:
<?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="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include layout="@layout/layout_line1"/>
<include layout="@layout/layout_line2"/>
</LinearLayout>
</LinearLayout>
layout_line1.xml和layout_line2.xml分别为第一行和第二行的布局,下面是layout_line1.xml的布局。layout_line2.xml除了控件的id名不同之外,其他的都一样,这里就不贴出来了。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:weightSum="4"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/ll_item1"
android:gravity="center"
android:padding="10dp"
android:orientation="vertical"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_icon1"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/tv_name1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="图标名称" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_item2"
android:gravity="center"
android:padding="10dp"
android:orientation="vertical"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_icon2"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/tv_name2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="图标名称" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_item3"
android:gravity="center"
android:padding="10dp"
android:orientation="vertical"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_icon3"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/tv_name3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="图标名称" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_item4"
android:gravity="center"
android:padding="10dp"
android:orientation="vertical"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_icon4"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/tv_name4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="图标名称" />
</LinearLayout>
</LinearLayout>
好了,我们所需要的布局都已经编写完毕,下面就正式开始写代码了!
4、编写代码
4.1 创建Bean类
创建一个NavigationItemBean类,用于存储图标的图片id、名称和选中状态等信息。
public class NavigationItemBean {
private int iconID;
private String iconName;
private boolean isSelected;
public NavigationItemBean(int iconID, String iconName) {
this.iconID = iconID;
this.iconName = iconName;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
public int getIconID() {
return iconID;
}
public void setIconID(int iconID) {
this.iconID = iconID;
}
public String getIconName() {
return iconName;
}
public void setIconName(String iconName) {
this.iconName = iconName;
}
}
4.2 设置图标资源
为了提高效率,我在初始化控件时使用了ButterKnife,大家需要它的jar包的话也可以从我的工程中复制粘贴。
public class MainActivity extends AppCompatActivity {
@Bind(R.id.vp_navigation)
CustomViewPager vpNavigation;
@Bind(R.id.ll_dots)
LinearLayout llDots;
private NavigationAdapter adapter;
private List<NavigationItemBean> itemBeanList = new ArrayList<>();
private List<ImageView> dots = new ArrayList<>();
//图标id数组
private int[] icons = {R.mipmap.phone,
R.mipmap.browser,
R.mipmap.messages,
R.mipmap.contacts,
R.mipmap.camera,
R.mipmap.gallery,
R.mipmap.calendar,
R.mipmap.calculator,
R.mipmap.settings,
R.mipmap.mail,
R.mipmap.maps,
R.mipmap.music,
R.mipmap.movie};
//图标名称数组
private String[] names = {"电话",
"浏览器",
"信息",
"联系人",
"照相",
"图库",
"日历",
"计算器",
"设置",
"邮箱",
"地图",
"音乐",
"电影"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
initData();
}
private void initData() {
for (int i = 0; i < icons.length; i++) {
itemBeanList.add(new NavigationItemBean(icons[i], names[i]));
}
adapter = new NavigationAdapter(this, itemBeanList, this);
vpNavigation.setAdapter(adapter);
}
}
先将图标的id和名称存储到两个数组中,再遍历数组将资源添加到集合中。
4.3 创建适配器
这一步应该是整个项目中最难的一步了,所以要打起精神来了。先看下面的代码:
public class NavigationAdapter extends PagerAdapter {
@Bind(R.id.iv_icon1)
ImageView ivIcon1;
@Bind(R.id.tv_name1)
TextView tvName1;
@Bind(R.id.ll_item1)
LinearLayout llItem1;
@Bind(R.id.iv_icon2)
ImageView ivIcon2;
@Bind(R.id.tv_name2)
TextView tvName2;
@Bind(R.id.ll_item2)
LinearLayout llItem2;
@Bind(R.id.iv_icon3)
ImageView ivIcon3;
@Bind(R.id.tv_name3)
TextView tvName3;
@Bind(R.id.ll_item3)
LinearLayout llItem3;
@Bind(R.id.iv_icon4)
ImageView ivIcon4;
@Bind(R.id.tv_name4)
TextView tvName4;
@Bind(R.id.ll_item4)
LinearLayout llItem4;
@Bind(R.id.iv_icon5)
ImageView ivIcon5;
@Bind(R.id.tv_name5)
TextView tvName5;
@Bind(R.id.ll_item5)
LinearLayout llItem5;
@Bind(R.id.iv_icon6)
ImageView ivIcon6;
@Bind(R.id.tv_name6)
TextView tvName6;
@Bind(R.id.ll_item6)
LinearLayout llItem6;
@Bind(R.id.iv_icon7)
ImageView ivIcon7;
@Bind(R.id.tv_name7)
TextView tvName7;
@Bind(R.id.ll_item7)
LinearLayout llItem7;
@Bind(R.id.iv_icon8)
ImageView ivIcon8;
@Bind(R.id.tv_name8)
TextView tvName8;
@Bind(R.id.ll_item8)
LinearLayout llItem8;
@Bind(R.id.ll_line2)
LinearLayout llLine2;
private Context context;
private List<NavigationItemBean> itemBeanList;
private List<View> itemViews;
int pages; //总页数
private View pageView;
private View.OnClickListener onClickListener;
public NavigationAdapter(Context context, List<NavigationItemBean> itemBeanList, View.OnClickListener onClickListener) {
this.context = context;
this.itemBeanList = itemBeanList;
this.onClickListener = onClickListener;
itemViews = new ArrayList<>();
initView(itemBeanList);
}
/**
* 初始化ViewPager的页面布局
* @param list
*/
private void initView(List<NavigationItemBean> list) {
itemViews.clear();
//计算ViewPager的页数
pages = list.size() / 9;
pages = pages + 1;
for (int i = 0; i < pages; i++) {
pageView = View.inflate(context, R.layout.page_navigation, null);
ButterKnife.bind(this, pageView);
setPagerViewData(i);
itemViews.add(pageView);
}
}
/**
* 根据页码来填充数据
* @param pageNum
*/
public void setPagerViewData(int pageNum) {
if (itemBeanList.size() > pageNum * 8 + 0) {
llItem1.setVisibility(View.VISIBLE);
tvName1.setText(itemBeanList.get(pageNum * 8 + 0).getIconName());
ivIcon1.setBackgroundResource(itemBeanList.get(pageNum * 8 + 0).getIconID());
setIconAlpha(itemBeanList.get(pageNum * 8 + 0).isSelected(), ivIcon1);
} else {
llItem1.setVisibility(View.INVISIBLE);
}
if (itemBeanList.size() > pageNum * 8 + 1) {
llItem2.setVisibility(View.VISIBLE);
tvName2.setText(itemBeanList.get(pageNum * 8 + 1).getIconName());
ivIcon2.setBackgroundResource(itemBeanList.get(pageNum * 8 + 1).getIconID());
setIconAlpha(itemBeanList.get(pageNum * 8 + 1).isSelected(), ivIcon2);
} else {
llItem2.setVisibility(View.INVISIBLE);
}
if (itemBeanList.size() > pageNum * 8 + 2) {
llItem3.setVisibility(View.VISIBLE);
tvName3.setText(itemBeanList.get(pageNum * 8 + 2).getIconName());
ivIcon3.setBackgroundResource(itemBeanList.get(pageNum * 8 + 2).getIconID());
setIconAlpha(itemBeanList.get(pageNum * 8 + 2).isSelected(), ivIcon3);
} else {
llItem3.setVisibility(View.INVISIBLE);
}
if (itemBeanList.size() > pageNum * 8 + 3) {
llItem4.setVisibility(View.VISIBLE);
tvName4.setText(itemBeanList.get(pageNum * 8 + 3).getIconName());
ivIcon4.setBackgroundResource(itemBeanList.get(pageNum * 8 + 3).getIconID());
setIconAlpha(itemBeanList.get(pageNum * 8 + 3).isSelected(), ivIcon4);
} else {
llItem4.setVisibility(View.INVISIBLE);
}
if (itemBeanList.size() > pageNum * 8 + 4) {
llItem5.setVisibility(View.VISIBLE);
tvName5.setText(itemBeanList.get(pageNum * 8 + 4).getIconName());
ivIcon5.setBackgroundResource(itemBeanList.get(pageNum * 8 + 4).getIconID());
setIconAlpha(itemBeanList.get(pageNum * 8 + 4).isSelected(), ivIcon5);
} else {
llItem5.setVisibility(View.INVISIBLE);
llLine2.setVisibility(View.GONE);
}
if (itemBeanList.size() > pageNum * 8 + 5) {
llItem6.setVisibility(View.VISIBLE);
tvName6.setText(itemBeanList.get(pageNum * 8 + 5).getIconName());
ivIcon6.setBackgroundResource(itemBeanList.get(pageNum * 8 + 5).getIconID());
setIconAlpha(itemBeanList.get(pageNum * 8 + 5).isSelected(), ivIcon6);
} else {
llItem6.setVisibility(View.INVISIBLE);
}
if (itemBeanList.size() > pageNum * 8 + 6) {
llItem7.setVisibility(View.VISIBLE);
tvName7.setText(itemBeanList.get(pageNum * 8 + 6).getIconName());
ivIcon7.setBackgroundResource(itemBeanList.get(pageNum * 8 + 6).getIconID());
setIconAlpha(itemBeanList.get(pageNum * 8 + 6).isSelected(), ivIcon7);
} else {
llItem7.setVisibility(View.INVISIBLE);
}
if (itemBeanList.size() > pageNum * 8 + 7) {
llItem8.setVisibility(View.VISIBLE);
tvName8.setText(itemBeanList.get(pageNum * 8 + 7).getIconName());
ivIcon8.setBackgroundResource(itemBeanList.get(pageNum * 8 + 7).getIconID());
setIconAlpha(itemBeanList.get(pageNum * 8 + 7).isSelected(), ivIcon8);
} else {
llItem8.setVisibility(View.INVISIBLE);
}
}
public void setIconAlpha(boolean isSelected, ImageView imageView) {
if (isSelected) {
imageView.setAlpha(1.0f);
} else {
imageView.setAlpha(0.4f);
}
}
@Override
public int getCount() {
return pages;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(itemViews.get(position));
return itemViews.get(position);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
代码有点长,但其实不难理解。首先我们创建一个NavigationAdapter继承于ViewPager专用的PageAdapter,并重写其中的几个方法。这里就要注意一个小地方了,getCount方法返回的是ViewPager的item数目,也就是页数,而不是图标的数目,可千万不能搞错了!
下面我就讲解一下比较重要的方法。首先是初始化ViewPager页面布局的方法initView:
private void initView(List<NavigationItemBean> list) {
itemViews.clear();
//计算ViewPager的页数
pages = list.size() / 9;
pages = pages + 1;
for (int i = 0; i < pages; i++) {
pageView = View.inflate(context, R.layout.page_navigation, null);
ButterKnife.bind(this, pageView);
setPagerViewData(i);
itemViews.add(pageView);
}
}
第4和第5行的代码是根据图标的数目来计算ViewPager的页数,而for循环则是有多少页我们就调用多少次View.inflate方法去创建布局。这也是ButterKnife.bind(this, pageView)
要在for循环中执行的原因:我们使用的虽然都是同一个布局文件,但实际每次循环都会创建一个新的布局。
setPagerViewData方法则是给页面中的每个item填充数据。首先判断集合中的某一位是否有数据,有的话则设置图片和文字,反之则隐藏item。里面的代码基本都一个样子,我就摘录其中“最特别”的一段了:
if (itemBeanList.size() > pageNum * 8 + 4) {
llItem5.setVisibility(View.VISIBLE);
tvName5.setText(itemBeanList.get(pageNum * 8 + 4).getIconName());
ivIcon5.setBackgroundResource(itemBeanList.get(pageNum * 8 + 4).getIconID());
setIconAlpha(itemBeanList.get(pageNum * 8 + 4).isSelected(), ivIcon5);
} else {
llItem5.setVisibility(View.INVISIBLE);
llLine2.setVisibility(View.GONE); //t图标数不大于4个时让第二行消失
}
如果你对于initView方法中for循环从0开始(页码的取值从0开始)而不是从1开始感到不解,那么看了setPagerViewData方法之后相信你就会懂了。页码为0时,第一页的第一个item取到的正好是集合的0位元素;页码为1时,第二页的第一个item则是集合中的第8位(1x8+1)元素,这样,我们就可以根据具体的页码来将集合中的元素填充到页面中。这里别忘了还有高度适应的问题。回头看看我摘出来的这段代码,当集合长度不大于4时,我们不仅要隐藏第五个item,而且要让第二行的布局消失,这时,ViewPager的高度就只有一行了。但你肯定会问,第一页时去掉第二行没什么,可以到了第二页时如果集合元素排不到第二行,第二行也会消失吗?答案是不会的。ViewPager默认所有页面的高度都是一致的,就算你在第二页就去掉了第二行,它的高度也不会随着改变。
4.4 设置导航点
在MainActivity中,我们创建绘制导航点的方法。导航点是使用xml文件画的,灰色为未选中,绿色为选中。
private List<ImageView> dots = new ArrayList<>();
/**
* 绘制导航点
*/
private void initDot() {
for (int i = 0; i < (itemBeanList.size() / 9 + 1); i++) {
Log.e("Tag", itemBeanList.size() + "");
ImageView imageView = new ImageView(this);
//创建一个ImageView来放置圆点,并确定ImageView的宽高
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
20, 20);
params.leftMargin = 10;
params.rightMargin = 10;
params.topMargin = 5;
params.bottomMargin = 10;
imageView.setLayoutParams(params);
dots.add(imageView);
//由于刚进去引导页时出现的是第一个引导页,故此时的导航点设置为红色
if (i == 0) {
dots.get(i).setBackgroundResource(R.drawable.dot_green);
} else {
dots.get(i).setBackgroundResource(R.drawable.dot_gray);
}
llDots.addView(imageView);
}
}
导航点的父布局是LinearLayout,所以我们使用LinearLayout中的LayoutParams给导航点设置上下左右的边距。在onCreate中调用该方法就可以绘制导航点了。因为ViewPager默认显示的是第一页,所以我们默认第一个的导航点是选中的。
除此之外,我们还需要让导航点起到导航的作用。这时候就可以去调用ViewPager的页面滑动监听了:
/**
* 滑动监听事件
*/
vpNavigation.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
Log.e("Tag", "position=" + position);
for (int i = 0; i < dots.size(); i++) {
if (i == position) {
dots.get(i).setBackgroundResource(R.drawable.dot_green);
} else {
dots.get(i).setBackgroundResource(R.drawable.dot_gray);
}
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
在选中某个页面时就将对应的导航点改为绿色就可以了。
4.5 图标的点击事件
页面布局中的控件都是在适配器NavigationAdapter中初始化的,为了在MainActivity中能够实现它们的点击事件,我们在NavigationAdapter的构造方法中传入一个View.onClickListener的对象作为参数,只需在MainActivity继承View.OnClickListener接口,在创建NavigationAdapter时传入this即可。
当然,我们还需要在NavigationAdapter中让需要实现点击事件的控件调用setOnClickListener方法:
private void initListener() {
llItem1.setOnClickListener(onClickListener);
llItem2.setOnClickListener(onClickListener);
llItem3.setOnClickListener(onClickListener);
llItem4.setOnClickListener(onClickListener);
llItem5.setOnClickListener(onClickListener);
llItem6.setOnClickListener(onClickListener);
llItem7.setOnClickListener(onClickListener);
llItem8.setOnClickListener(onClickListener);
}
大家想一想,initListener方法应该在哪里调用呢?这里有一个坑,由于ViewPager的页面是一页一页地绘制的,所以注册控件的点击事件也应该在for循环中,为每一页的控件都注册一遍:
for (int i = 0; i < pages; i++) {
pageView = View.inflate(context, R.layout.page_navigation, null);
ButterKnife.bind(this, pageView);
setPagerViewData(i);
itemViews.add(pageView);
initListener();
}
好了,现在可以到MainActivity中实现监听事件了:
@Override
public void onClick(View v) {
int pageNum = vpNavigation.getCurrentItem();
Log.e("Tag", "viewPager.getCurrentItem()=" + vpNavigation.getCurrentItem());
switch (v.getId()) {
case R.id.ll_item1:
Toast.makeText(this, itemBeanList.get(pageNum * 8 + 0).getIconName(), Toast.LENGTH_SHORT).show();
initSelected();
itemBeanList.get(pageNum * 8 + 0).setSelected(true);
adapter.refresh(itemBeanList);
break;
case R.id.ll_item2:
Toast.makeText(this, itemBeanList.get(pageNum * 8 + 1).getIconName(), Toast.LENGTH_SHORT).show();
initSelected();
itemBeanList.get(pageNum * 8 + 1).setSelected(true);
adapter.refresh(itemBeanList);
break;
case R.id.ll_item3:
Toast.makeText(this, itemBeanList.get(pageNum * 8 + 2).getIconName(), Toast.LENGTH_SHORT).show();
initSelected();
itemBeanList.get(pageNum * 8 + 2).setSelected(true);
adapter.refresh(itemBeanList);
break;
case R.id.ll_item4:
Toast.makeText(this, itemBeanList.get(pageNum * 8 + 3).getIconName(), Toast.LENGTH_SHORT).show();
initSelected();
itemBeanList.get(pageNum * 8 + 3).setSelected(true);
adapter.refresh(itemBeanList);
break;
case R.id.ll_item5:
Toast.makeText(this, itemBeanList.get(pageNum * 8 + 4).getIconName(), Toast.LENGTH_SHORT).show();
initSelected();
itemBeanList.get(pageNum * 8 + 4).setSelected(true);
adapter.refresh(itemBeanList);
break;
case R.id.ll_item6:
Toast.makeText(this, itemBeanList.get(pageNum * 8 + 5).getIconName(), Toast.LENGTH_SHORT).show();
initSelected();
itemBeanList.get(pageNum * 8 + 5).setSelected(true);
adapter.refresh(itemBeanList);
break;
case R.id.ll_item7:
Toast.makeText(this, itemBeanList.get(pageNum * 8 + 6).getIconName(), Toast.LENGTH_SHORT).show();
initSelected();
itemBeanList.get(pageNum * 8 + 6).setSelected(true);
adapter.refresh(itemBeanList);
break;
case R.id.ll_item8:
Toast.makeText(this, itemBeanList.get(pageNum * 8 + 7).getIconName(), Toast.LENGTH_SHORT).show();
initSelected();
itemBeanList.get(pageNum * 8 + 7).setSelected(true);
adapter.refresh(itemBeanList);
break;
default:
break;
}
}
/**
* 初始化图标的选择状态,全部设置为未选中状态
*/
private void initSelected() {
for (int i = 0; i < itemBeanList.size(); i++) {
itemBeanList.get(i).setSelected(false);
}
}
刷新Adapter时我是重新调用了initView方法,并且传入了修改后的数据源。下面是在Adapter中刷新的方法:
public void refresh(List<NavigationItemBean> itemBeanList){
initView(itemBeanList);
notifyDataSetChanged();
}
运行一下,点击,咦,怎么没有反应呢?PageAdapter这里有一个坑了:notifyDataSetChanged只有在ViewPager的页数发生变化才会刷新页面,单是某一个页面的数据发生变化时是没用的。但是不用着急,我们可以重写getItemPosition方法,让其返回POSITION_NONE,强迫viewpager重绘所有的页面。
/**
* 使Adapter能够刷新布局
*/
private int mChildCount = 0;
@Override
public void notifyDataSetChanged() {
mChildCount = getCount();
super.notifyDataSetChanged();
}
@Override
public int getItemPosition(Object object) {
if (mChildCount > 0) {
mChildCount--;
return POSITION_NONE;
}
return super.getItemPosition(object);
}
运行一下,点击,发现可以完美实现我们要的效果了。
4.6 双击取消选中
实现双击取消选中的效果并不难,我们只需在点击每一个图标之前都判断一下当前的选择状态,如果当前为选中的状态,则将状态改为未选中,反之,则改为选中。示例代码如下:
if (itemBeanList.get(pageNum * 8 + 0).isSelected()) {
initSelected();
itemBeanList.get(pageNum * 8 + 0).setSelected(false);
} else {
initSelected();
itemBeanList.get(pageNum * 8 + 0).setSelected(true);
}
我在代码中只设置了前面两项,大家如果想实现所有图标的双击选中事件的话就自己动一下手吧!
5、总结
这个功能我前前后后花了不少时间,在参考别人代码的基础上总算是实现了,水平有限,如果博客中有错漏的地方,欢迎批评指正。我的写法还是太过简单粗暴,不够精巧,而且对资源的占用也比较大,如果你有更好的方法,欢迎交流学习。听说ViewPager+GridVie也可以实现同样的效果,接下来我再研究一下,看能不能再写一篇博客吧!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。