之前一段时间在做列表的交互功能,UI要求做成GridView,后来又要求做成每一页固定的可以翻页的GridView。下面贴个Demo:
Demo:可翻页的GridView(错误)
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.GridView;
import android.widget.SimpleAdapter;
public class MainActivity extends Activity
{
int line = 2;
int column =6;
int page = 1;
ArrayList<Map<String,Object>> sublist = new ArrayList<Map<String,Object>>();
SimpleAdapter adapter_01;
GridView gv;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button pagedown = (Button)findViewById(R.id.pagedown);
final Button pageup = (Button)findViewById(R.id.pageup);
gv = (GridView)findViewById(R.id.gv);
gv.setNumColumns(column);
gv.setVerticalSpacing(50);
final String [] number = new String[]
{
"1","2","3","4","5","6"
,"7","8","9","10",
"11","12","13","14","15","16","17","18","19","20",
"21","22","23","24","25","26"
,"27","28","29","30",
"31","32","33","34","35","36","37","38","39","40"
};
final ArrayList<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
for(int i=0;i<number.length;i++)
{
Map<String,Object> listitem = new HashMap<String,Object>();
listitem.put("id", number[i]);
list.add(listitem);
}
sublist = getlist(list, page, line, column);
pageup.setClickable(false);
if(sublist.size()<line*column) pagedown.setClickable(false);
adapter_01 = new SimpleAdapter(this
,sublist
,R.layout.list
,new String[]{"id"}
,new int[]{R.id.tv});
gv.setAdapter(adapter_01);
pagedown.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
page++;
sublist.clear();
sublist = getlist(list, page, line, column);
gv.setAdapter(adapter_01);
adapter_01.notifyDataSetChanged();
Log.v("sublist",""+sublist);
pageup.setClickable(true);
if(sublist.size()<line*column)
{
pagedown.setClickable(false);
}
else
{
pagedown.setClickable(true);
}
}
});
pageup.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
page--;
sublist.clear();
sublist = getlist(list, page, line, column);
gv.setAdapter(adapter_01);
adapter_01.notifyDataSetChanged();
Log.v("sublist",""+sublist);
pagedown.setClickable(true);
if(page==1)
{
pageup.setClickable(false);
}
else
{
pageup.setClickable(true);
}
}
});
}
public ArrayList<Map<String,Object>> getlist(ArrayList<Map<String,Object>> list,int page,int line,int column)
{
ArrayList<Map<String,Object>> sublist = new ArrayList<Map<String,Object>>();
for(int k=0,i=(page-1)*line*column;i<list.size()&&k<line*column;i++,k++)
{
sublist.add(list.get(i));
}
return sublist;
}
}
以上程序本来是要实现GridView翻页显示,每一页显示一个2行6列的子GridView——即第一页显示1~12,第二页显示2~24,以此类推。但是单击pagedown按钮翻页,发现显示了一个空白页。查看sublist的Log输出:
sublist的内容确实已经更新了,可是为什么没有显示出来?怀疑的对象也只能是adapter了。之前也用过adapter的notifydatasetchanged()方法更新list,并没有出现类似的问题,百思不得其解后,先找了个方法先把功能实现了,以进行后续的工作:
1 pagedown.setOnClickListener(new OnClickListener()
2 {
3 public void onClick(View v)
4 {
5 page++;
6 sublist = getlist(list, page, line, column);
7 SimpleAdapter adapter_01 = new SimpleAdapter(MainActivity.this
8 ,sublist
9 ,R.layout.list
10 ,new String[]{"id"}
11 ,new int[]{R.id.tv});
12 gv.setAdapter(adapter_01);
13 adapter_01.notifyDataSetChanged();
14 pageup.setClickable(true);
15 if(sublist.size()<line*column)
16 {
17 pagedown.setClickable(false);
18 }
19 else
20 {
21 pagedown.setClickable(true);
22 }
23 }
24 });
看到这不要笑我,我也知道每翻一页就新建一个adapter,效率之低下。。。只是为了不打断后面的工作,硬着头皮写上去的。这样写之后,也确实可以实现预期的功能了。
然而出来混,总是要还的,终于问题到了非解决不可得时候。我将代码与之前成功的例子仔细比较,发现以前的更新,都是为列表添加/删除操作——即add,remove之类,而这次的关键语句是通过getlist方法直接获得下一页的子表。问题就出在这里。
1)sublist这个名字,表示的是一个引用,存储在栈内存空间中,当它第一次调用getlist方法时,它就指向了堆内存的某个空间,这个空间存储的是getlist方法获得的第一个子表(第一页);
2)创建adapter,将sublist作为其第二个参数存入,关键就是这里,原本以为adapter存入的是sublist这个引用,但这只是自己一厢情愿。先看SimpleAdapter源码:
1 public class SimpleAdapter extends BaseAdapter implements Filterable {
2 private int[] mTo;
3 private String[] mFrom;
4 private ViewBinder mViewBinder;
5
6 private List<? extends Map<String, ?>> mData;
7
8 private int mResource;
9 private int mDropDownResource;
10 private LayoutInflater mInflater;
11
12 private SimpleFilter mFilter;
13 private ArrayList<Map<String, ?>> mUnfilteredData;
14
15 /**
16 * Constructor
17 *
18 * @param context The context where the View associated with this SimpleAdapter is running
19 * @param data A List of Maps. Each entry in the List corresponds to one row in the list. The
20 * Maps contain the data for each row, and should include all the entries specified in
21 * "from"
22 * @param resource Resource identifier of a view layout that defines the views for this list
23 * item. The layout file should include at least those named views defined in "to"
24 * @param from A list of column names that will be added to the Map associated with each
25 * item.
26 * @param to The views that should display column in the "from" parameter. These should all be
27 * TextViews. The first N views in this list are given the values of the first N columns
28 * in the from parameter.
29 */
30 public SimpleAdapter(Context context, List<? extends Map<String, ?>> data,
31 int resource, String[] from, int[] to) {
32 mData = data;
33 mResource = mDropDownResource = resource;
34 mFrom = from;
35 mTo = to;
36 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
37 }
38 ......
39 }
看到了吧,上面SimpleAdapter的构造器,把传入的第二个参数赋给了mData,也就此时mData指向了第一个子表(第一页)的堆内存空间,然而改变了sublist之后,sublist已经指向了第二个子表(第二页)的堆内存空间,可是mData仍然指向原来的对内存空间,adapter的notifydatasetchanged()方法仅仅是更新mData指向的数据,所以出现了上述现象。
下面来验证这个说法。先吐槽一下,java中没有类似C语言的‘&’——地址操作符,个人感觉不是很方便啊。不过还好Object中使用equals方法,比较的就是地址,可以用这个方法判断sublist=getlist()前后是否改变了指向的地址。
将程序改为
1 pageup.setOnClickListener(new OnClickListener()
2 {
3 public void onClick(View v)
4 {
5 page--;
6 ArrayList<Map<String,Object>> a = sublist;
7 sublist.clear();
8 Log.v("before:a.equals(sublist)",""+a.equals(sublist));
9 sublist = getlist(list, page, line, column);
10 Log.v("before:a.equals(sublist)",""+a.equals(sublist));
11 SimpleAdapter adapter_01 = new SimpleAdapter(MainActivity.this
12 ,sublist
13 ,R.layout.list
14 ,new String[]{"id"}
15 ,new int[]{R.id.tv});
16 gv.setAdapter(adapter_01);
17 adapter_01.notifyDataSetChanged();
18 pagedown.setClickable(true);
19 if(page==1)
20 {
21 pageup.setClickable(false);
22 }
23 else
24 {
25 pageup.setClickable(true);
26 }
27 }
28 });
其日志打印为
果然前后两次不同了。在将程序进行修改:
1 pagedown.setOnClickListener(new OnClickListener()
2 {
3 public void onClick(View v)
4 {
5 page++;
6 ArrayList<Map<String,Object>> a = sublist;
7 sublist.clear();
8 Log.v("before:a.equals(sublist)",""+a.equals(sublist));
9 for(int i = 0;i < getlist(list, page, line, column).size(); i++)
10 {
11 sublist.add(getlist(list, page, line, column).get(i));
12 }
13 Log.v("before:a.equals(sublist)",""+a.equals(sublist));
14 adapter_01.notifyDataSetChanged();
15 pageup.setClickable(true);
16 if(sublist.size()<line*column)
17 {
18 pagedown.setClickable(false);
19 }
20 else
21 {
22 pagedown.setClickable(true);
23 }
24 }
25 });
这就实现了不改变sublist的指向,从程序的运行结果看果然
java的基础才是对android的最可靠保证啊,对于java看了1个月就学android的我来说,尤其如此。。