用到Adapter时,有时会直接继承BaseAdapter,这样子类代码会显得冗杂,我们可以封装一个MyAdapter继承BaseAdapter,然后我们写一个SonAdapter继承MyAdapter。充分发挥java语言封装特性。具体而言,这样做有其三大优势:
1.简化子类代码:让每个子类中(SonAdapter)一模一样方法(如getItem,getPostion,getCount)直接在MyAdapter这个父类中补全,不必再子类中赘述,简化子类代码;
2.不同代码子类具体实现: getView在每个子类中不尽相同,通过接口回调方式让该方法依据子类布局控件于子类代码中具体实现。
3.丰富功能:在MyAdapter中可以增加一些方法,相当于为子类预装一些特有功能,方便调用,如下文中的addDatas(),clearData()等。
MyAdapter中具体代码实现如下:
package org.moblie.trian.testbaseadapter.adapter;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
public abstract class MyAdapter<T> extends BaseAdapter {
private List<T> mDatas;
private LayoutInflater mInflater;
public MyAdapter(List<T> datas, Context context) {
super();
mDatas = datas;
// 直接调用底层方法,效率更高。避免将该过程放到getView中重复创建。
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
// 添加数据源,并刷新界面。
public void addDatas(List<T> datas) {
// 用if判断一下,防止当datas为空时,更新界面时抛出空指针异常,提高了基类代码健壮性。
if (datas != null && datas.size() > 0) {
mDatas.addAll(datas);
notifyDataSetChanged();
//思考一下:为什么要设置list!=null并且list.size()>0,二者有何不同呢?
}
}
// 清空数据源,并刷新界面
public void clearData() {
mDatas.clear();
notifyDataSetChanged();
}
// 通过三目运算符判断一下,健壮并丰满代码。
public int getCount() {
return mDatas == null ? 0 : mDatas.size();
}
@Override
public Object getItem(int position) {
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
// 以上三个方法子类父类写法完全一样,直接写到父类,避免子类代码过多冗杂。
// 但是getView方法在每个子类中不尽相同,通过接口回调的方式,让getView方法的在子类中根据子类的布局控件再具体实现。
public View getView(int position, View convertView, ViewGroup parent) {
return getMyView(position, convertView, parent, mDatas, mInflater);
}
// 将子类中需要用的mDatas,mInflater通过抽象方法参数列表的方式传递给子类
// 系统只认识getView【其参数列表已被固定】,我们需要新的参数列表,只有用到接口回调,写一个自己的抽象方法
public abstract View getMyView(int position, View convertView,ViewGroup parent, List<T> mDatas, LayoutInflater mInflater);
}
其子类的代码:
package org.moblie.trian.testbaseadapter;
import java.util.List;
import java.util.Map;
import org.moblie.trian.testbaseadapter.adapter.MyAdapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class SonAdapter extends MyAdapter<Map<String, String>> {
private List<Map<String, String>> datas;
public SonAdapter(List<Map<String, String>> datas, Context context) {
super(datas, context);
this.datas = datas;
}
public View getMyView(int position, View convertView, ViewGroup parent,
List<Map<String, String>> mDatas, LayoutInflater mInflater) {
ViewsHolder holder = null;
if (convertView == null) {
holder = new ViewsHolder();
convertView = mInflater.inflate(R.layout.item, parent, false);
holder.content = (TextView) convertView.findViewById(R.id.content);
// 给首次创建的convertView设置标签,便于重复利用,并赋值
convertView.setTag(holder);
} else {
// 重复利用非空的convertView,重新给它赋值。
holder = (ViewsHolder) convertView.getTag();
}
// 给TextView设置值。
holder.content.setText(datas.get(position).get("content"));
return convertView;
}
public class ViewsHolder {
private TextView content;
}
}
MainActivity中的代码:
1.json糗事百科分条依次解析
2.右下角置顶键,通过onClick属性设置。
package org.moblie.trian.testbaseadapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.moblie.trian.myUtil.HttpURLConnHelper;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;
public class MainActivity extends Activity {
private ListView listView;
private SonAdapter adapter;
private List<Map<String, String>> list = new ArrayList<Map<String, String>>();
private boolean isBottom;
private int currentPage = 1;
private String jsonUrl = "http://m2.qiushibaike.com/article/list/suggest?page=";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化控件
initViews();
// 下载网络数据
loadNetDatas();
}
public void toTopClick(View v) {
listView.setSelection(0);
}
private void loadNetDatas() {
new MyTask().execute(jsonUrl + currentPage);
}
private void initViews() {
// listView控件初始化
listView = (ListView) findViewById(R.id.listView_main);
// Adapter初始化
adapter = new SonAdapter(list, this);
// 给listView绑定适配器
listView.setAdapter(adapter);
// 给listView绑定监听
listView.setOnScrollListener(new OnScrollListener() {
public void onScrollStateChanged(AbsListView view, int scrollState) {
// scrollState三种状态,SCROLL_STATE_IDLE为 闲置
if (isBottom && scrollState == SCROLL_STATE_IDLE) {
// 页码加1之后再加载
currentPage++;
loadNetDatas();
}
}
// firstVisibleItem:此时屏幕上最上端那个item的position值
// visibleItemCount:屏幕上可见的Item条数
// totalItemCount:该页中item总的条数
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// 判断是否到page底部
isBottom = ((firstVisibleItem + visibleItemCount) == totalItemCount);
}
});
}
// 解析json是一个耗时操作,需要在子线程完成
class MyTask extends AsyncTask<String, Integer, List<Map<String, String>>> {
protected List<Map<String, String>> doInBackground(String... params) {
byte[] data = HttpURLConnHelper.loadByteFromURL(params[0]);
if (data != null) {
String jsonString = new String(data);
List<Map<String, String>> li = jsonStringToList(jsonString);
return li;
} else {
return null;
}
}
@Override
protected void onPostExecute(List<Map<String, String>> li) {
super.onPostExecute(li);
adapter.addDatas(li);
}
}
List<Map<String, String>> jsonStringToList(String jsonString) {
try {
JSONObject jsonObject = new JSONObject(jsonString);
JSONArray jsonArray_items = jsonObject.getJSONArray("items");
for (int i = 0; i < jsonArray_items.length(); i++) {
String jsonObject_content = jsonArray_items.getJSONObject(i)
.getString("content");
Map<String, String> map = new HashMap<String, String>();
map.put("content", jsonObject_content);
list.add(map);
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return list;
}
}
activity_main中的代码:
button设置onClick属性,点击置顶
<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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<ListView
android:id="@+id/listView_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/toTop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="toTopClick"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:text="点我置顶"
/>
</RelativeLayout>
item中的代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
最后一定要记得在清单文件中设置网络权限。
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.INTERNET"/>
在此特别说明一下list!=null和list.size()>0的区别:
1.list==null,意味着list压根没有地址,在堆内就不存在。
2.list.size()=0 意思堆内有list但是还没来得及放元素,其长度随着元素数量变化而变化,暂时为零。
3.list如果为null的话,说明没有进行初始化。这是list调用任何的方法都会抛出空异常。list.size()==0说明list已经被new过,但是里面没有值。
4.区别:
①有没有瓶子 list != null
②瓶子里有没有水 list.isEmpty()
判断的时候一定要注意先后顺序
如果没有瓶子都没有,直接判断有没有水,是会报nullException的
③另外:
list.add(null)
会造成list.isEmpty() 为 false, list.size() 为1
所以代码里要避免list.add(null)的陷阱
④比较形象一个比喻:
举个形象的例子,我有一个空着的水杯(list),而你没有,那你是null,我的size为0。你想装水需要去买个水杯(new ArrayList();),我就可以直接装水(list.add(水))。你要是没有杯子直接倒水,水就流出去啦(空指针异常)。所以用做判断的时候经常连用 list!=null && list.size()!=0 。