Android利用ViewPager+GridView,实现网格布局(功能列表)并能水平滑动

首先看一下效果图,根据效果图,分析实现原理与思路:


首先分析这个功能:它的界面是一个类似于网格形式的功能列表(二行四列),每个单元格里面存放这个各个功能的一级菜单。
然后可以通过手指水平滑动该网格,能够进入下一页的功能列表。
反复思考了下............
1.网格形式的实现可以通过GirdView控件。但是,这里要注意的是GirdView本身无法实现网格线。因此,我们必须解决
如何"在GridView中添加网格线?"
2.左右滑动,我们可以使用ViewPager控件。

大体思路
我们之所以选择使用GridView作为每个ViewPager的页面,是考虑到:当这些分类条目的数据集发生变化时,比较容易的动态更新。(通过SDK工具查看美团的View层级,发现其也是使用ViewPager里放入两个GridView实现。)


假设(模拟)数据有20条,即20个类目,先考虑一下每个GridView页面有几条几列,这里我们暂定为每页8条4列分类,则ViewPager一共有 20条/8条每页 =2.5 ,取整为3页。

这里有个疑点就是,“怎么将GridView和ViewPager合并,并在ViewPager翻页时,这个GridView能正确显示数据?”,这里打了个引号“”,代表这个说法有问题,其实并不是将两个控件合并。ViewPager这里没有特殊处理,只是将GridView作为View传给ViewPager的Adapter,比如:刚才计算,如果一共三页,则会inflate出三个GridView作为每页的View加入集合中,并将这个集合作为ViewPager的数据源传给ViewPager的Adapter。 这是回答了如何将GridView和ViewPager合并产生关系。

那么如何在ViewPager翻页时,怎么才能保证那个GridView显示正确的数据呢?一开始我的想法是监听ViewPager的翻页事件,然后再修改GridView的数据集,然后再更新视图.............
很遗憾,这个想法是错误的。

正确做法如下四步:这里需要将GridView的Adapter处理一下,给Adapter传入的数据集mDatas就是条目的总数据集,不用修改。
一:增加两个属性,index和pageSize,代表页数和每一页显示的最大条目上限。在给这三页的GridView设置Adapter时,
传入当前页数index,然后经过计算得出正确的count和应该显示的View的数据。

二:修改getCount()方法,

/**  * 先判断数据集的大小是否足够显示满本页?mDatas.size() > (mIndex+1)*mPageSize,  * 如果够,则直接返回每一页显示的最大条目个数mPageSize,  * 如果不够,则有几项返回几,(mDatas.size() - mIndex * mPageSize);  */ @Override
public int getCount() {
    return mDatas.size() > (mIndex + 1) * mPageSize ? mPageSize : (mDatas.size() - mIndex * mPageSize);
}
先判断数据集的大小是否足够显示满本页?mDatas.size() > (mIndex+1)*mPageSize,如果够,则直接返回每一页显示的最大条目个数mPageSize,如果不够,则有几项返回几项(mDatas.size() - mIndex * mPageSize);
三:修改getView()方法:

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    Log.i("TAG", "position:" + position);
    ViewHolder vh = null;
    if (convertView == null) {
        convertView = mLayoutInflater.inflate(R.layout.girdview_item, parent, false);
        vh = new ViewHolder();
        vh.tv = (TextView) convertView.findViewById(R.id.id_tv_title);
        vh.iv = (ImageView) convertView.findViewById(R.id.id_iv_icon);
        convertView.setTag(vh);
    } else {
        vh = (ViewHolder) convertView.getTag();
    }
    /**  * 在给View绑定显示的数据时,计算正确的position = position + mIndex * mPageSize */  int pos = position + mIndex * mPageSize;
    vh.tv.setText(mDatas.get(pos).name);
    vh.iv.setImageResource(mDatas.get(pos).iconRes);

    convertView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Toast.makeText(context,"第"+mIndex+"页,Item"+position,Toast.LENGTH_SHORT).show();
        }
    });
    return convertView;
}
在给View绑定显示的数据时,根据当前下标index,和每页显示最大的条目数pageSize ,计算一下正确的position。
四:其实第四步不修改,显示也是正常的,但是我觉得应该也要同步修改一下,getItem和getItemId方法,代码一并贴上来。

@Override
public Object getItem(int position) {
    return mDatas.get(position + mIndex * mPageSize);
}

@Override
public long getItemId(int position) {
    return position + mIndex * mPageSize;
}
完整的GridViewAdapter.java代码

package com.csii.myapplication;

import android.content.Context;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;

/**  * Created by bitaotao on 2016/6/30.  */ public class GridViewAdapter extends BaseAdapter {
    private List<HeaderViewBean> mDatas;
    private LayoutInflater mLayoutInflater;
    private Context context;
    private int columnWidth;
    /**  * 页数下标,从0开始  */  private int mIndex;
    /**  * 每页显示最大条目个数 ,默认是dimes.xml里 HomePageHeaderColumn 属性值的两倍  */  private int mPageSize;

    /**  * @param context 上下文  * @param mDatas 传递的数据  * @param columnWidth  * @param mIndex 页码  */  public GridViewAdapter(Context context, List<HeaderViewBean> mDatas, int columnWidth, int mIndex) {
        this.context = context;
        this.mDatas = mDatas;
        mLayoutInflater = LayoutInflater.from(context);
        this.mIndex = mIndex;
        mPageSize =context.getResources().getInteger(R.integer.HomePageHeaderColum) * 2;
        this.columnWidth= columnWidth;
    }

    public GridViewAdapter(Context context, List<HeaderViewBean> mDatas, int index) {
        this.context = context;
        this.mDatas = mDatas;
        mLayoutInflater = LayoutInflater.from(context);
        this.mIndex = index;
        mPageSize =context.getResources().getInteger(R.integer.HomePageHeaderColum) * 2;
    }

    /**  * 先判断数据集的大小是否足够显示满本页?mDatas.size() > (mIndex+1)*mPageSize,  * 如果够,则直接返回每一页显示的最大条目个数mPageSize,  * 如果不够,则有几项返回几,(mDatas.size() - mIndex * mPageSize);  */  @Override
    public int getCount() {
        return mDatas.size() > (mIndex + 1) * mPageSize ? mPageSize : (mDatas.size() - mIndex * mPageSize);
    }

    @Override
    public Object getItem(int position) {
        return mDatas.get(position + mIndex * mPageSize);
    }

    @Override
    public long getItemId(int position) {
        return position + mIndex * mPageSize;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        Log.i("TAG", "position:" + position);
        ViewHolder vh = null;
        if (convertView == null) {
            convertView = mLayoutInflater.inflate(R.layout.girdview_item, parent, false);
            vh = new ViewHolder();
            vh.tv = (TextView) convertView.findViewById(R.id.id_tv_title);
            vh.iv = (ImageView) convertView.findViewById(R.id.id_iv_icon);
            convertView.setTag(vh);
        } else {
            vh = (ViewHolder) convertView.getTag();
        }
        /**  * 在给View绑定显示的数据时,计算正确的position = position + mIndex * mPageSize,  */  int pos = position + mIndex * mPageSize;
        vh.tv.setText(mDatas.get(pos).name);
        vh.iv.setImageResource(mDatas.get(pos).iconRes);

        convertView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(context,"第"+mIndex+"页,Item"+position,Toast.LENGTH_SHORT).show();
            }
        });
        return convertView;
    }


    class ViewHolder {
        public TextView tv;
        public ImageView iv;
    }
}
HeadViewBean.java

package com.csii.myapplication;

/**  * Created by bitaotao on 2016/6/30.  */ public class HeaderViewBean {
    public String name;
    public int iconRes;
    public HeaderViewBean(String name, int iconRes) {
        this.name = name;
        this.iconRes = iconRes;
    }
}
完成GridViewAdapter的编写,基本上已经完成60%了。下面就是如何关联ViewPager和GridView:

MyViewPagerAdapter.java

package com.csii.myapplication;

import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;

import java.util.List;

/**  * Created by bitaotao on 2016/6/30.  */ public class MyViewPagerAdapter extends PagerAdapter {
    private List<View> mViewList;

    public MyViewPagerAdapter(List<View> mViewList) {
        this.mViewList = mViewList;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView(mViewList.get(position));
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        container.addView(mViewList.get(position));
        return (mViewList.get(position));
    }

    @Override
    public int getCount() {
        if (mViewList == null)
            return 0;
        return mViewList.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }
}
MainActivity.java

package com.csii.myapplication;

import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.GridView;
import android.widget.RelativeLayout;

import java.security.Policy;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private ViewPager mViewPager;

    private List<View> mViewPagerGridList;
    private List<HeaderViewBean> mDatas = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mViewPager = (ViewPager) findViewById(R.id.id_vp);
        initDatas();
        mViewPagerGridList = new ArrayList<>();
        LayoutInflater inflater = getLayoutInflater();

        // 每页显示最大条目个数
        int pageSize =getResources().getInteger(R.integer.HomePageHeaderColum) * 2;
        //页数
        int pageCount = (int) Math.ceil(mDatas.size()*1.0/pageSize);
        //获取屏幕的宽度,单位px
        int screenWidth  = getResources().getDisplayMetrics().widthPixels;
        //获取GridView中每个item的宽度 = 屏幕宽度 / GridView显示的列数
       int columnWidth = (int) Math.ceil((screenWidth)*1.0 / (getResources().getInteger(R.integer.HomePageHeaderColum)) );

        for(int index = 0;index < pageCount;index++){
            GridView grid = (GridView) inflater.inflate(R.layout.gridview_layout,mViewPager,false);
            //设置GridView每个item的宽度
            grid.setColumnWidth(columnWidth);
            //设置GirdView的布局参数(宽和高,宽为包裹父容器,高 = columnWidth)
            grid.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,columnWidth));
            grid.setAdapter(new GridViewAdapter(this,mDatas,index));
            mViewPagerGridList.add(grid);
        }
        mViewPager.setAdapter(new MyViewPagerAdapter(mViewPagerGridList));
    }

    private void initDatas() {
        for (int i=0;i<23;i++){
            HeaderViewBean headerViewBean = new HeaderViewBean("Item "+(i+1),R.drawable.dinghuohuzhuan_normal);
            mDatas.add(headerViewBean);
        }
    }
}
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context="com.csii.myapplication.MainActivity">

    <android.support.v4.view.ViewPager
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/id_vp"/>
</RelativeLayout>

gridview_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:stretchMode="columnWidth"
    android:numColumns="4"
    android:horizontalSpacing="1dp"
    android:verticalSpacing="1dp"
    android:gravity="center"
    android:background="#DCDCDC"
    android:listSelector="@null"
    android:layout_height="wrap_content">
</GridView>
gridview_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:background="#fff"
    android:orientation="vertical">
    <ImageView
        android:layout_width="wrap_content"
        android:id="@+id/id_iv_icon"
        android:layout_marginLeft="5dp"
        android:background="@drawable/dinghuohuzhuan_normal"
        android:layout_marginRight="5dp"
        android:layout_height="wrap_content" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Item 1"
        android:id="@+id/id_tv_title"
        android:gravity="center"
        android:textColor="#f00"
        android:padding="5dp"
        android:textSize="14sp" />
</LinearLayout>
Android利用ViewPager+GridView,实现网格布局(功能列表)并能水平滑动_第1张图片

参考文章:http://blog.csdn.net/zxt0601/article/details/50675489

Android中GridView控件如何添加网格线?

非常感谢这两位大神。在此,小弟谢过了。(PS:小弟整理下,方便以后使用)。


你可能感兴趣的:(Android利用ViewPager+GridView,实现网格布局(功能列表)并能水平滑动)