Android仿饿了么首页导航栏(ViewPager)

klxklx 7年前
   <h2>1、需求分析</h2>    <p>在饿了么首页中我们能看到这样的布局(如下图)。红框内是一个可以左右滑动的页面,每一个页面类似于九宫格,都有可供点击图标。对于这样的布局,我在网上找了很久都没有找到相关的名称,所以我这里暂时叫它导航页吧。</p>    <p><img src="https://simg.open-open.com/show/6e947eebbd09d2015ae39f330524cfe7.jpg"></p>    <p>最近公司的项目就要求我实现一个这样的布局,但是我们的图标并不是想饿了么这样是固定的,所以在饿了么的布局上还要加一个效果:在图标数目无法排满两行时,就只显示一行。比如说,我们每一页最多可以显示两行和四列,当图标的总数目小于或等于4个时就只显示出一行,第二行就不要了。这样页面就不至于留出太多的空白。</p>    <p>先梳理一下我们要实现的效果:</p>    <ol>     <li> <p>大体的框架是一个可以左右滑动的页面;</p> </li>     <li> <p>每一页为两行四列的矩阵;</p> </li>     <li> <p>底部有和页面数目相等的导航点,且滑动到某个页面时,相应的导航点会改变颜色;</p> </li>     <li> <p>单选事件:选中图标后,图标变为完全不透明。</p> </li>    </ol>    <p>明白需求之后,我们穿越时空看看最后实现后的布局:</p>    <p><img src="https://simg.open-open.com/show/d296be028165980e089e6ae3b506198b.gif"></p>    <p>图片资源大家可以在我源码中找到。</p>    <h2>2、实现思路</h2>    <p>我们已经分析了需求,现在就来分析一下怎么实现。</p>    <p>必须事先说明一下,我的实现方法比较暴力,也比较占资源,所以大家要是有更好的方法的话欢迎留言告诉我,我们互相学习。</p>    <p>首先,这是一个左右滑动的页面,所以我们可以考虑使用ViewPager我们每个页面有8个图标,也就是8个item。在图标的数目在4以下时就只显示第一行,所以布局上我们可以将四个item作为一行放置到横向的线性布局中,当第二行没有图标时就让它消失。</p>    <p>ViewPager中的每一页的布局都是一样,所以我们可以对其复用:将图标分组,每8个为一组(余下的不到八页也归为一组),每一组即为一页。每次滑动的页面时就加载布局,填充图标和文字。</p>    <p>下面的导航点就比较简单了,可以使用ViewPager的滑动监听事件进行监听,滑动到选中的页面时就改变导航点的颜色。</p>    <p>至于透明,我们只需要记录图标的选中状态,然后改变透明度即可。</p>    <h2>3、编写布局</h2>    <p>接下来就让我们创建项目,编写页面布局吧。</p>    <p>在Android Studio中创建一个NavigationPager项目,MainActivity的布局如下:</p>    <h3>activity_main.xml</h3>    <pre>  <code class="language-java"><?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></code></pre>    <p>MainActivity的主布局是一个纵向的线性布局,上面一个ViewPager,下面一个横向的线性布局,用于放置导航点。这里得说一下ViewPager的特性,在不指定特定高度的情况下,ViewPager的高度是默认填充整个父布局的,我们显然不希望这样,毕竟我们还要分一行和两行两种情况呢。所以这里我在这里使用了一个可以自适应高度的自定义ViewPager:</p>    <h3>CustomViewPager</h3>    <pre>  <code class="language-java">/**   * 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);      }  }</code></pre>    <p>页面的布局(也就是ViewPager的子布局)可以拆分成两个横向排列的布局:</p>    <pre>  <code class="language-java"><?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></code></pre>    <p>layout_line1.xml和layout_line2.xml分别为第一行和第二行的布局,下面是layout_line1.xml的布局。layout_line2.xml除了控件的id名不同之外,其他的都一样,这里就不贴出来了。</p>    <pre>  <code class="language-java"><?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></code></pre>    <p>好了,我们所需要的布局都已经编写完毕,下面就正式开始写代码了!</p>    <h2>4、编写代码</h2>    <h3>4.1 创建Bean类</h3>    <p>创建一个NavigationItemBean类,用于存储图标的图片id、名称和选中状态等信息。</p>    <pre>  <code class="language-java">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;      }  }</code></pre>    <h3>4.2 设置图标资源</h3>    <p>为了提高效率,我在初始化控件时使用了ButterKnife,大家需要它的jar包的话也可以从我的工程中复制粘贴。</p>    <pre>  <code class="language-java">public class MainActivity extends AppCompatActivity  {        @Bind(R.id.vp_navigation)      CustomViewPager V*Navigation;      @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);          V*Navigation.setAdapter(adapter);      }  }</code></pre>    <p>先将图标的id和名称存储到两个数组中,再遍历数组将资源添加到集合中。</p>    <h3>4.3 创建适配器</h3>    <p>这一步应该是整个项目中最难的一步了,所以要打起精神来了。先看下面的代码:</p>    <pre>  <code class="language-java">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);      }  }</code></pre>    <p>代码有点长,但其实不难理解。首先我们创建一个NavigationAdapter继承于ViewPager专用的PageAdapter,并重写其中的几个方法。这里就要注意一个小地方了,getCount方法返回的是ViewPager的item数目,也就是页数,而不是图标的数目,可千万不能搞错了!</p>    <p>下面我就讲解一下比较重要的方法。首先是初始化ViewPager页面布局的方法initView:</p>    <pre>  <code class="language-java">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);          }      }</code></pre>    <p>第4和第5行的代码是根据图标的数目来计算ViewPager的页数,而for循环则是有多少页我们就调用多少次View.inflate方法去创建布局。这也是 ButterKnife.bind(this, pageView) 要在for循环中执行的原因:我们使用的虽然都是同一个布局文件,但实际每次循环都会创建一个新的布局。</p>    <p>setPagerViewData方法则是给页面中的每个item填充数据。首先判断集合中的某一位是否有数据,有的话则设置图片和文字,反之则隐藏item。里面的代码基本都一个样子,我就摘录其中“最特别”的一段了:</p>    <pre>  <code class="language-java">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个时让第二行消失          }</code></pre>    <p>如果你对于initView方法中for循环从0开始(页码的取值从0开始)而不是从1开始感到不解,那么看了setPagerViewData方法之后相信你就会懂了。页码为0时,第一页的第一个item取到的正好是集合的0位元素;页码为1时,第二页的第一个item则是集合中的第8位(1x8+1)元素,这样,我们就可以根据具体的页码来将集合中的元素填充到页面中。这里别忘了还有高度适应的问题。回头看看我摘出来的这段代码,当集合长度不大于4时,我们不仅要隐藏第五个item,而且要让第二行的布局消失,这时,ViewPager的高度就只有一行了。但你肯定会问,第一页时去掉第二行没什么,可以到了第二页时如果集合元素排不到第二行,第二行也会消失吗?答案是不会的。ViewPager默认所有页面的高度都是一致的,就算你在第二页就去掉了第二行,它的高度也不会随着改变。</p>    <h3>4.4 设置导航点</h3>    <p>在MainActivity中,我们创建绘制导航点的方法。导航点是使用xml文件画的,灰色为未选中,绿色为选中。</p>    <pre>  <code class="language-java">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);          }      }</code></pre>    <p>导航点的父布局是LinearLayout,所以我们使用LinearLayout中的LayoutParams给导航点设置上下左右的边距。在onCreate中调用该方法就可以绘制导航点了。因为ViewPager默认显示的是第一页,所以我们默认第一个的导航点是选中的。</p>    <p>除此之外,我们还需要让导航点起到导航的作用。这时候就可以去调用ViewPager的页面滑动监听了:</p>    <pre>  <code class="language-java">/**           * 滑动监听事件           */          V*Navigation.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) {                }          });</code></pre>    <p>在选中某个页面时就将对应的导航点改为绿色就可以了。</p>    <h3>4.5 图标的点击事件</h3>    <p>页面布局中的控件都是在适配器NavigationAdapter中初始化的,为了在MainActivity中能够实现它们的点击事件,我们在NavigationAdapter的构造方法中传入一个View.onClickListener的对象作为参数,只需在MainActivity继承View.OnClickListener接口,在创建NavigationAdapter时传入this即可。</p>    <p>当然,我们还需要在NavigationAdapter中让需要实现点击事件的控件调用setOnClickListener方法:</p>    <pre>  <code class="language-java">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);      }</code></pre>    <p>大家想一想,initListener方法应该在哪里调用呢?这里有一个坑,由于ViewPager的页面是一页一页地绘制的,所以注册控件的点击事件也应该在for循环中,为每一页的控件都注册一遍:</p>    <pre>  <code class="language-java">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();          }</code></pre>    <p>好了,现在可以到MainActivity中实现监听事件了:</p>    <pre>  <code class="language-java">@Override      public void onClick(View v) {          int pageNum = V*Navigation.getCurrentItem();          Log.e("Tag", "viewPager.getCurrentItem()=" + V*Navigation.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);          }      }</code></pre>    <p>刷新Adapter时我是重新调用了initView方法,并且传入了修改后的数据源。下面是在Adapter中刷新的方法:</p>    <pre>  <code class="language-java">public void refresh(List<NavigationItemBean> itemBeanList){          initView(itemBeanList);          notifyDataSetChanged();      }</code></pre>    <p>运行一下,点击,咦,怎么没有反应呢?PageAdapter这里有一个坑了:notifyDataSetChanged只有在ViewPager的页数发生变化才会刷新页面,单是某一个页面的数据发生变化时是没用的。但是不用着急,我们可以重写getItemPosition方法,让其返回POSITION_NONE,强迫viewpager重绘所有的页面。</p>    <pre>  <code class="language-java">/**       * 使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);      }</code></pre>    <p>运行一下,点击,发现可以完美实现我们要的效果了。</p>    <h3>4.6 双击取消选中</h3>    <p>实现双击取消选中的效果并不难,我们只需在点击每一个图标之前都判断一下当前的选择状态,如果当前为选中的状态,则将状态改为未选中,反之,则改为选中。示例代码如下:</p>    <pre>  <code class="language-java">if (itemBeanList.get(pageNum * 8 + 0).isSelected()) {                      initSelected();                      itemBeanList.get(pageNum * 8 + 0).setSelected(false);                  } else {                      initSelected();                      itemBeanList.get(pageNum * 8 + 0).setSelected(true);                  }</code></pre>    <p>我在代码中只设置了前面两项,大家如果想实现所有图标的双击选中事件的话就自己动一下手吧!</p>    <h2>5、总结</h2>    <p>这个功能我前前后后花了不少时间,在参考别人代码的基础上总算是实现了,水平有限,如果博客中有错漏的地方,欢迎批评指正。我的写法还是太过简单粗暴,不够精巧,而且对资源的占用也比较大,如果你有更好的方法,欢迎交流学习。听说ViewPager+GridVie也可以实现同样的效果,接下来我再研究一下,看能不能再写一篇博客吧!</p>    <p>最后是源码下载地址:</p>    <p><a href="/misc/goto?guid=4959749483271943221" rel="nofollow,noindex">GitHub</a></p>    <p> </p>    <p>来自:http://blog.csdn.net/Lindroid20/article/details/66968410</p>    <p> </p>