BaseAdapter使用比较麻烦,它是个抽象类,需要重写4个方法分别是getCount() getItem(..) getItemId(..) getVew(..),相应的使用BaseAdapter
可以自由的定义自己想要的布局,先看看程序效果图:
这个布局一共有5个元素分别是:ImageViw 2个TextView CheckBox Button
先看这个ListView的Item布局文件list_item.xml:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_gravity="center_horizontal" android:layout_width="match_parent" android:layout_height="50dp"> <ImageView android:layout_width="wrap_content" android:layout_height="match_parent" android:src="@drawable/ic_launcher" android:id="@+id/iv_image"/> <!-- 注意这个里两个TextView的布局是垂注布局的--> <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="vertical" android:layout_weight="4"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:textSize="25dp" android:text="hah" android:id="@+id/tv_item"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:text="content" android:id="@+id/tv_content"/> </LinearLayout> <CheckBox android:layout_width="wrap_content" android:layout_height="match_parent" android:id="@+id/ck_box" /> <Button android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="1" android:textSize="20sp" android:text="Select" android:id="@+id/btn_select" android:background="@drawable/btn_selector"/> </LinearLayout>
整理一下我们要做的事情:1、首先在主布局里要放一个ListView;
2、准备ListView显示需要的数据;
3、重写适配器,将布局文件与数据放到ListView中显示,这一部分最复杂,还有布局文件中还有点击事件需要设置。
下面是主程序代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.skymaster.hs.baseadaptertest; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; import java.util.HashMap; public class MainActivity extends AppCompatActivity { private ListView listView; private HsAdapter hsAdapter; private ArrayList<HashMap<String,Object>> listItem; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = (ListView) findViewById(R.id.lv_list); hsAdapter = new HsAdapter(getLayoutInflater()); DataInit(); listView.setAdapter(hsAdapter); } /* * 初始化显示的内容ListView表的数据结构对应的是ArrayList * 但是ListView内部每一项Item有细分多个View,所以ArrayList泛型定义为Map * 这样就可以定义多中数据类型了,没一个key对应一个View,适配器在填充内容的时候 * 可以根据key来获取内容,这里要注意一点:HashMap<String,Object> hashMap = new HashMap<>(); * 一定要放到循环里面,因为ListView有多少个Item就应该有多少个数据对象也就是说有多少new出来得对象实例 * */ private void DataInit(){ listItem = new ArrayList<>(); for(int i=0;i<10;i++){ HashMap<String,Object> hashMap = new HashMap<>(); hashMap.put("title","title:"+i); hashMap.put("content","content:"+i); hashMap.put("image",image_index[i]); listItem.add(hashMap); } } /* * 这里定义ImageView的数组索引,适配器在设置ImageView背景的时候使用比较方便 * */ private final int[] image_index = { R.drawable.ic_launcher, R.drawable.image1, R.drawable.image2, R.drawable.image3, R.drawable.image4, R.drawable.image5, R.drawable.image6, R.drawable.image7, R.drawable.image8, R.drawable.image9, }; /* * convertView配合ViewHolder可以提高UI加载的效率,这里定义成static型 * 相当于全局保存布局文件中的View对象实例。 * */ static class ViewHolder{ TextView tv_item; TextView tv_content; ImageView iv_image; Button btn_select; CheckBox ck_box; } private class HsAdapter extends BaseAdapter{ /* * 这里需要解释一下LayoutInflater的用法:/** * Instantiates a layout XML file into its corresponding {@link android.view.View} * objects. It is never used directly. Instead, use * {@link android.app.Activity#getLayoutInflater()} or * {@link Context#getSystemService} to retrieve a standard LayoutInflater instance * that is already hooked up to the current context and correctly configured * for the device you are running on. * 以上是源码的注释 * 首先这个是一个抽象对象,所以从来不会直接实例化来引用,而且它也没有任何的子类。 * 所以要使用这个类,唯一的办法就是:1、从Activity.getLayoutInflater()中获得 * 2、从上下文Context.getSystemService找回当前上下文连接的 * LayoutInflater实例。 * eg:LayoutInflater inflater = (LayoutInflater)context.getSystemService * (Context.LAYOUT_INFLATER_SERVICE) * 因此我们设置适配器构造函数的时候传入了Activity的布局解析器 * */ private LayoutInflater mInflator; private ViewHolder viewHolder; public HsAdapter(LayoutInflater mInflator){ this.mInflator = mInflator; } @Override public int getCount() { return listItem.size(); } @Override public Object getItem(int position) { System.out.println("getItem"); return null; } @Override public long getItemId(int position) { System.out.println("getItemId:"+position); return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { if(convertView == null){ /* * 1.这段代码执行的次数与ListView的项数有关系,如果手机屏幕最大显示数目是6个,那么执行的次数 * 就是7次,最后会保留一个convertView实例在viewHolder中,并且保存在convertView的Tag中, * 这样做的目的是当ListView滑动,显示变化需要加载新的Item的时候可以不用再执行这一段代码 * 直接用tag中保存的引用来填充Item,提高效率。这就是setTag()的目的。 * 2.注意viewHolder = new ViewHolder();与convertView = mInflator.inflate(R.layout.list_item, null); * 是对应的,不然会引起空指针错误。 * 3.一段代码加载的原理就是:在ListView初始化的时候每加载一项Item都会执行一次getView,并且这个时候 * convertView都是空,如果反过来理解其实每一项viewHolder的引用都必须通过findViewById来指向一个实例,不然就会报错。 * 当屏幕最大显示的6项加载完毕,在多保留一个viewHolder引用备用就不会执行了。这个时候如果滑动ListView * 需要加载新的Item,这个时候又会调用getView,而这个时候会直接用预留的一个convertView。如果继续滑动, * 这个时候convertView并不为空,这是因为滑动的过程中除了会出现新的Item,旧的Item会移出屏幕不在显示,这个时候 * convertView其实装载的是旧的引用,如此循环,因此可以判断如果手机整个屏幕ListView最大显示的 * item的数据是n那么convertView的引用数据就是n+1了 * */ viewHolder = new ViewHolder(); convertView = mInflator.inflate(R.layout.list_item, null); viewHolder.tv_item = (TextView)convertView.findViewById(R.id.tv_item); viewHolder.iv_image = (ImageView)convertView.findViewById(R.id.iv_image); viewHolder.tv_content = (TextView)convertView.findViewById(R.id.tv_content); viewHolder.btn_select = (Button)convertView.findViewById(R.id.btn_select); viewHolder.ck_box = (CheckBox)convertView.findViewById(R.id.ck_box); viewHolder.btn_select.setTag(position);//设置按键的 Tag辅助判断是哪一个按键按下,因为这里所有项的按键实体id只有一个所有需要 //position来辅助判断 viewHolder.btn_select.setOnClickListener(new KeyPressService());//设置按键监听一定要放在里面,因为getView调用的次数非常多 //而我们只需要对有效Item的按键设置监听。 viewHolder.ck_box.setTag(position);//同理按键 viewHolder.ck_box.setOnClickListener(new KeyPressService());//同理按键 convertView.setTag(viewHolder);//保存引用 }else{ viewHolder = (ViewHolder)convertView.getTag(); viewHolder.btn_select.setTag(position); viewHolder.btn_select.setOnClickListener(new KeyPressService()); viewHolder.ck_box.setOnClickListener(new KeyPressService()); viewHolder.ck_box.setTag(position); } /* * 这里就是适配器绑定数据源与View的过程。利用Map的数据结构可以方便找到 * 因为是HashMap<String,Object>返回的value是object所以要强转 * */ viewHolder.iv_image.setBackgroundResource((Integer) listItem.get(position).get("image")); viewHolder.tv_item.setText((String) listItem.get(position).get("title")); viewHolder.tv_content.setText((String) listItem.get(position).get("content")); return convertView; } } private class KeyPressService implements View.OnClickListener{ @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_select: ClickEventProcess(v,"Button"); break; case R.id.ck_box: ClickEventProcess(v,"checkbox"); break; } } private void ClickEventProcess(View v,String str){ switch ((Integer)v.getTag()){ case 0: Toast.makeText(MainActivity.this,"click:0"+str,Toast.LENGTH_SHORT) .show(); break; case 1: Toast.makeText(MainActivity.this,"click:1"+str,Toast.LENGTH_SHORT) .show(); break; case 2: Toast.makeText(MainActivity.this,"click:2"+str,Toast.LENGTH_SHORT) .show(); break; case 3: Toast.makeText(MainActivity.this,"click:3"+str,Toast.LENGTH_SHORT) .show(); break; case 4: Toast.makeText(MainActivity.this,"click:4"+str,Toast.LENGTH_SHORT) .show(); break; case 5: Toast.makeText(MainActivity.this,"click:5"+str,Toast.LENGTH_SHORT) .show(); break; case 6: Toast.makeText(MainActivity.this,"click:6"+str,Toast.LENGTH_SHORT) .show(); break; case 7: Toast.makeText(MainActivity.this,"click:7"+str,Toast.LENGTH_SHORT) .show(); break; case 8: Toast.makeText(MainActivity.this,"click:8"+str,Toast.LENGTH_SHORT) .show(); break; case 9: Toast.makeText(MainActivity.this,"click:9"+str,Toast.LENGTH_SHORT) .show(); break; } } } }
下面是程序执行的效果:
主界面的布局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" 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="com.skymaster.hs.baseadaptertest.MainActivity"> <ListView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/lv_list"/> </RelativeLayout>
按键的drawable文件btn_normal.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="45dp"/>
<size android:height="10dp"
android:width="30dp"/>
<solid android:color="@color/colorPrimary"/>
</shape>
btn_press.xml:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <corners android:radius="45dp"/> <size android:height="10dp" android:width="30dp"/> <solid android:color="@color/colorPrimary"/> <stroke android:color="#ff0000" android:width="10dp"/> </shape>
btn_selector.xml:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@drawable/btn_press"/> <item android:state_pressed="false" android:drawable="@drawable/btn_normal"/> </selector>