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>

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

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

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


  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
ViewPager和Fragment可以很好地实现底部导航的功能。下面是一些实现方法: 1. 创建一个包含ViewPager和底部导航的布局文件,底部导航可以使用TabLayout或者自定义的布局。 2. 创建Fragment类,每个Fragment表示一个底部导航项对应的页面内容。 3. 创建PagerAdapter类,继承自FragmentPagerAdapter或者FragmentStatePagerAdapter,用于管理ViewPager中的Fragment。 4. 在Activity中找到ViewPager和TabLayout(或者自定义的底部导航),设置PagerAdapter和TabLayout的关联。 5. 实现TabLayout的OnTabSelectedListener接口,在回调方法中切换ViewPager的当前页面。 6. 在Fragment中添加需要展示的布局和逻辑代码。 下面是一个示例代码: MainActivity.java ``` public class MainActivity extends AppCompatActivity implements TabLayout.OnTabSelectedListener { private ViewPager viewPager; private TabLayout tabLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewPager = findViewById(R.id.viewPager); tabLayout = findViewById(R.id.tabLayout); PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(adapter); tabLayout.setupWithViewPager(viewPager); tabLayout.addOnTabSelectedListener(this); } @Override public void onTabSelected(TabLayout.Tab tab) { viewPager.setCurrentItem(tab.getPosition()); } @Override public void onTabUnselected(TabLayout.Tab tab) { } @Override public void onTabReselected(TabLayout.Tab tab) { } private static class PagerAdapter extends FragmentPagerAdapter { private static final int NUM_PAGES = 3; public PagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { switch (position) { case 0: return new HomeFragment(); case 1: return new NewsFragment(); case 2: return new SettingsFragment(); default: return null; } } @Override public int getCount() { return NUM_PAGES; } @Override public CharSequence getPageTitle(int position) { switch (position) { case 0: return "Home"; case 1: return "News"; case 2: return "Settings"; default: return null; } } } } ``` activity_main.xml ``` <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.view.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent"/> <android.support.design.widget.TabLayout android:id="@+id/tabLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:background="@android:color/white"/> </RelativeLayout> ``` HomeFragment.java ``` public class HomeFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_home, container, false); // 添加布局和逻辑代码 return view; } } ``` NewsFragment.java ``` public class NewsFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_news, container, false); // 添加布局和逻辑代码 return view; } } ``` SettingsFragment.java ``` public class SettingsFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_settings, container, false); // 添加布局和逻辑代码 return view; } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上善若水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值