概述
GridView是一种有效显示大量图片的列表容器,用法也跟ListView类似,它的布局是一个网格,一行可以有多个项,并且整个视图可以滚动,我们常见的应用有手机中的图库、launcher里面的应用列表、类似微信多张图片等,总的来说,ListView主要应用于单列多行的列表,然而GridView主要应用于多行多列的网状布局,它能够一次显示许多图片,同时即将被显示的图片会处于准备显示的状态。
GridView显示图片
下面是一个典型的使用场景,在Fragment里面内置GridView,其中GridView的子视图是ImageView。
实现效果:我们让图片以网格的样式显示出来,点击图片,则显示这张图片。
应用明细部分的实现
我们首先完成应用的明细部分,即显示某张选中的图片。
设计一个名为ImageDetailFragment的Fragment来管理下图的用户界面,再设计一个名为ImageDetailActivity的activity来托管ImageDetailFragment实例。这里我们使用动态加载的方式。
Step1 :定义定义容器视图
修改ImageDetailActivity的布局文件如下,fragment_activity.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
FrameLayout是服务于ImageDetailFragment的容器视图。注意容器视图是通用性视图,不局限于ImageDetailFragment类,我们可以并且也将使用同一个布局来托管其他的fragment。
step 2:定义 ImageDetailFragment 的布局
布局文件 fragment_image_detail.xml,用于显示某张具体的图片:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/mImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@mipmap/ic_launcher"/>
</LinearLayout>
step 3:创建 ImageDetailFragment类
public class ImageDetailFragment extends Fragment {
private ImageView mImageView;
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_deatil_image,container,false);
mImageView = (ImageView)v.findViewById(R.id.mImageView);
return v;
}
...
}
step 4:添加UI Fragment到FragmentManager
public class ImageDetailActivity extends SingleFragmentActivity {
public static final String EXTRA_IMAGE = "extra_image";
...
@Override
protected Fragment createFragment() {
return new ImageDetailFragment();
}
}
这里我们把托管UI Fragment的代码抽取出来,封装成一个通用的抽象类SingleFragmentActivity类,这样子我们下一次托管另一个Fragment的时候就方便多了。
SingleFragmentActivity.java:
public abstract class SingleFragmentActivity extends FragmentActivity {
protected abstract Fragment createFragment();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_activity);
FragmentManager fm = getFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
if (fragment == null) {
fragment = createFragment();
fm.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
}
}
GridView显示图片部分的实现
step 1:实现ImageGridActivity类
public class ImageGridActivity extends SingleFragmentActivity {
@Override
protected Fragment createFragment() {
return new ImageGridFragment();
}
}
step 2:创建ImageGridFragment类
public class ImageGridFragment extends Fragment {
// Nothing yet
}
step 3:创建GridView布局
fragment_image_grid.xml :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<GridView
android:id="@+id/photo_wall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnWidth="90dp"
android:stretchMode="columnWidth"
android:numColumns="auto_fit"
android:verticalSpacing="10dip"
android:gravity="center"></GridView>
</LinearLayout>
android:numColumns=”auto_fit” //GridView的列数设置为自动
android:columnWidth=”90dp ” //每列的宽度,也就是Item的宽度
android:stretchMode=”columnWidth”//缩放与列宽大小同步
android:verticalSpacing=”10dp” //两行之间的边距
step 3:创建GridView子项布局
layout_grid_item.xml :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_head"
android:layout_width="90dp"
android:layout_height="90dp"
android:layout_centerInParent="true"
android:scaleType="centerCrop"/>
</LinearLayout>
step 4:为ImageGridFragment配置视图(ImageGridFrament.java):
public class ImageGridFragment extends Fragment {
public final static Integer[] imageResIds = new Integer[] {
R.mipmap.simple_image_1, R.mipmap.simple_image_2, R.mipmap.simple_image_3,
R.mipmap.simple_image_4, R.mipmap.simple_image_5, R.mipmap.simple_image_6,
R.mipmap.simple_image_7, R.mipmap.simple_image_8, R.mipmap.simple_image_9};
public ImageGridFragment(){
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.fragment_image_grid,container,false);
final GridView mGridView = (GridView) v.findViewById(R.id.photo_wall);
return v;
}
...
}
step 4:为ImageGridFragment实现Adapter和点击事件(ImageGridFrament.java):
public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
private ImageAdapter mAdapter;
public final static Integer[] imageResIds = new Integer[] {
R.mipmap.simple_image_1, R.mipmap.simple_image_2, R.mipmap.simple_image_3,
R.mipmap.simple_image_4, R.mipmap.simple_image_5, R.mipmap.simple_image_6,
R.mipmap.simple_image_7, R.mipmap.simple_image_8, R.mipmap.simple_image_9};
public ImageGridFragment(){
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAdapter = new ImageAdapter(getActivity());
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.fragment_image_grid,container,false);
final GridView mGridView = (GridView) v.findViewById(R.id.photo_wall);
mGridView.setAdapter(mAdapter);
//设置监听器
mGridView.setOnItemClickListener(this);
return v;
}
@Override
//点击事件
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(getActivity(),"you clicked!",Toast.LENGTH_SHORT).show();
}
}
private class ImageAdapter extends BaseAdapter{
private final Context mContext;
public ImageAdapter(Context context) {
super();
this.mContext = context;
}
@Override
public int getCount() {
return imageResIds.length;
}
@Override
public Object getItem(int position) {
return imageResIds[position];
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
ImageView imageView;
if(convertView==null) {
LayoutInflater inflater = getActivity().getLayoutInflater();
convertView = inflater.inflate(R.layout.layout_grid_item,parent,false);
viewHolder = new ViewHolder();
viewHolder.imageView = (ImageView)convertView.findViewById(R.id.iv_head);
convertView.setTag(viewHolder);
}
else{
viewHolder = (ViewHolder)convertView.getTag();
}
viewHolder.imageView.setImageResource(imageResIds[position]);
return convertView;
}
private class ViewHolder {
ImageView imageView;
}
}
}
适配器的用法和ListView几乎一模一样。
关联列表和明细
应用的列表和明细我们都已经完成了,接下来要做的就是如何关联两个部分。
step 1:从Fragment中启动Activity( ImageGridFragment ):
从 fragment 中 启 动 activity 类 似 于 从 activity 中 启 动 activity 。 我 们 调 用 Fragment.startActivity(Intent)方法,然后在后台触发调用对应的Activity方法。
在ImageGridFragment的Adapter类里, 用启动ImageDetailActivity实例的代码,替换Toast消息处理代码,
public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
...
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent i = new Intent(getActivity(), ImageDetailActivity.class);
startActivity(i);
}
private class ImageAdapter extends BaseAdapter{
...
}
}
指定要启动的activity为ImageDetailActivity, ImageGridFragment创建了一个显式intent。至于Intent构造方法需要的Context对象, ImageGridFragment是使用getActivity()方法传入它
的托管activity来满足的。
step 2:附加 extra 信息( ImageDetailActivity ):
现在我们点击任意一张图片都能进入到ImageDetailActivity类,但是因为不知道用户点击的是哪张图片,因此ImageDetailActivity总是会显示一张默认的ic_luncher图片。
启动ImageDetailActivity时,传递附加到Intent extra上的int position, ImageDetailFragment就能知道该显示哪张图片,这需要在ImageDetailActivity中新增newIntent方法:
public class ImageDetailActivity extends SingleFragmentActivity {
public static final String EXTRA_IMAGE = "extra_image";
public static Intent newIntent(Context packageContext, int imagePosition){
Intent intent = new Intent(packageContext,ImageDetailActivity.class);
intent.putExtra(EXTRA_IMAGE,imagePosition);
return intent;
}
...
}
创建了显式intent后,调用putExtra(…)方法,传入匹配position的字符串键与键值。 这里,由于position是Integer对象,我们需要调用putExtra(String,Int)方法。
step 3:更新OnItenonClick()方法,使用newIntent新方法( ImageGrideFragment):
public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
...
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent i = ImageDetailActivity.newIntent(getActivity(), position);
startActivity(i);
}
...
}
step 3:获取 extra 信息( ImageDetailFragment):
我们在onItemClick()方法中调用newIntent()方法时,传入用户点击的图片的位置position,现在position已经安全地存储到ImageDetailActivity的Intent中,然而,要获取和使用extra信息的是ImageDetailFragment类。
我们使用fragment argument来获取extra数据,看这里
编写newInstance(int)方法(ImageDetailFragment.java)
public class ImageDetailFragment extends Fragment {
private static final String IMAGE_DATA_EXTRA = "resId";
private int mImageNum;
private ImageView mImageView;
public static ImageDetailFragment newInstance(int imageNum){
Bundle args = new Bundle();
args.putInt(IMAGE_DATA_EXTRA,imageNum);
ImageDetailFragment fragment = new ImageDetailFragment();
fragment.setArguments(args);
return fragment;
}
...
}
使用newInstance(Int)方法( ImageDetailActvity ):
public class ImageDetailActivity extends SingleFragmentActivity {
public static final String EXTRA_IMAGE = "extra_image";
...
@Override
protected Fragment createFragment() {
int position = getIntent().getIntExtra(EXTRA_IMAGE,0);
return ImageDetailFragment.newInstance(position);
}
}
现在我们就将int position传入了argument之中。
step 4:获取 argument 中的数据( ImageDetailFragment):
public class ImageDetailFragment extends Fragment {
private static final String IMAGE_DATA_EXTRA = "resId";
private int mImageNum;
private ImageView mImageView;
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mImageNum = getArguments() != null ? getArguments().getInt(IMAGE_DATA_EXTRA) : -1;
}
...
}
至此我们就完成了我们的效果:
使用ViewPager
在应用中我们有时也会使用滑动来切换显示不同图片,我们可以通过PagerAdapter与ViewPager控件来实现这个效果。 不过,一个更加合适的Adapter是PagerAdapter的一个子类,叫FragmentStatePagerAdapter:它可以在某个ViewPager中的子视图切换出屏幕时自动销毁与保存Fragments的状态。这样能够保持更少的内存消耗。
如果只有为数不多的图片并且确保不会超出程序内存限制,那么使用PagerAdapter或 FragmentPagerAdapter会更加合适。
step 1:创建ImagePagerActivity:
我们新建一个名为ImagePagerActivity的新建activity取代ImageDetailActivity。其布局由一个ViewPager组成。
image_detail_pager.xml:
<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.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
</LinearLayout>
step 2:创建ViewPager(ImagePagerActivity.java):
public class ImagePagerActivity extends FragmentActivity {
public static final String EXTRA_IMAGE = "extra_image";
private ImagePagerAdapter mAdapter;
private ViewPager mPager;
private int imagePosition;
// A static dataset to back the ViewPager adapter
public final static Integer[] imageResIds = new Integer[] {
R.mipmap.simple_image_1, R.mipmap.simple_image_2, R.mipmap.simple_image_3,
R.mipmap.simple_image_4, R.mipmap.simple_image_5, R.mipmap.simple_image_6,
R.mipmap.simple_image_7, R.mipmap.simple_image_8, R.mipmap.simple_image_9};
public static Intent newIntent(Context packageContext, int imagePosition){
Intent intent = new Intent(packageContext,ImagePagerActivity.class);
intent.putExtra(EXTRA_IMAGE,imagePosition);
return intent;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_detail_pager); // Contains just a ViewPager
mPager = (ViewPager) findViewById(R.id.pager);
}
...
}
step 3:设置pager adapter(ImagePagerActivity.java):
public class ImagePagerActivity extends FragmentActivity {
public static final String EXTRA_IMAGE = "extra_image";
private ImagePagerAdapter mAdapter;
private ViewPager mPager;
private int imagePosition;
// A static dataset to back the ViewPager adapter
public final static Integer[] imageResIds = new Integer[] {
R.mipmap.simple_image_1, R.mipmap.simple_image_2, R.mipmap.simple_image_3,
R.mipmap.simple_image_4, R.mipmap.simple_image_5, R.mipmap.simple_image_6,
R.mipmap.simple_image_7, R.mipmap.simple_image_8, R.mipmap.simple_image_9};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_detail_pager); // Contains just a ViewPager
mAdapter = new ImagePagerAdapter(getFragmentManager(), imageResIds.length);
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
}
public static class ImagePagerAdapter extends FragmentStatePagerAdapter {
private final int mSize;
public ImagePagerAdapter(FragmentManager fm, int size) {
super(fm);
mSize = size;
}
@Override
public int getCount() {
return mSize;
}
@Override
public Fragment getItem(int position) {
return ImageDetailFragment.newInstance(position);
}
}
}
FragmentStatePagerAdapter化繁为简,提供了两个有用的方法: getCount()和getItem(int)。调用getItem(int)方法,获取并显示imageResIds数组中指定位置的图片时,它会返回配置过的ImageDetailFragment来完成显示任务。
FragmentStatePagerAdapter是我们的代理,负责管理与ViewPager的对话并协同工作。代理需首先将getItem(int)方法返回的fragment添加给activity,然后才能使用fragment完成自己的工作。这也就是创建代理实例时,需要FragmentManager的原因。
(代理究竟做了哪些工作呢?简单来说,就是将返回的fragment添加给托管activity,并帮助viewpager找到fragment的视图并一一对应。)
pager adapter的两个方法简单直接。 getCount()方法返回数组列表中包含的列表项数目。
getItem(int)方法有着神奇的魔法。它首先获取数据集中指定位置的图片实例,然后利用该图片的位置创建并返回一个有效配置的ImageDetailFragment。
step4:配置启动ImagePagerActivity(ImageGrideFragment.java):
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//Intent i = GridDetailActivity.newIntent(getActivity(), position);
Intent i = ImagePagerActivity.newIntent(getActivity(),position);
startActivity(i);
}
至此我们就完成了图片的滑动,但是有一个小问题,每次点击图片都会显示第一张图片,那是因为ViewPager默认只显示PageAdapter中的第一个列表项。要显示选中的列表项,可设置ViewPager当前要显示的列表项为涂片数组中指定位置的列表项。
step5:设置初始分页显示项(ImagePagerActivity.java):
public class ImageDetailActivity extends FragmentActivity {
public static final String EXTRA_IMAGE = "extra_image";
private ImagePagerAdapter mAdapter;
private ViewPager mPager;
private int imagePosition;
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image_detail_pager); // Contains just a ViewPager
//获得存储在Intent中的点击位置
imagePosition = getIntent().getIntExtra(EXTRA_IMAGE,0);
...
//设置显示指定位置的列表项即图片
mPager.setCurrentItem(imagePosition);
}
...
}
有了这两行代码,选择任意图片,其对应的图片应该能够显示了,并且左右滑动可以查看其他图片。
源码在这里。有点大,主要是图片的锅~
本文详细介绍GridView与ViewPager在Android开发中的应用。GridView适用于多行多列的网状布局,展示大量图片;ViewPager则常用于实现图片滑动效果。文章通过具体步骤演示如何实现这两种组件,并介绍如何在点击GridView中的图片时,跳转到ViewPager展示选中的图片。

384

被折叠的 条评论
为什么被折叠?



