以前在开发Android App界面时,每使用一个ListView就要对应写一个适配器,简直痛苦万分啊。今天有空来实现在一个自动绑定数据的ListView适配器。代码的AS版本Gradle plugin 是3.5.3,Gradle是5.4.1。这代码能不能在别的版本上运行,我还真没有试过。
前提条件:
- 第一条也是最重要的一条,就是你已经能很熟练地做一个手动绑定的适配器。
- 控件命名必须有一定的规则,以方便查找。我经常命名如 txt_xxx是TextView,img_XXX是ImageView等等。
- 传入适配器构造函数的数据是map数组:List<Map<String,Object>> mapLists,其中每个map中的key是txt_xxx中的xxx,Obect存放要控件需要赋的数值。
思路如下:
第一步:在适配的getViewb函数中,遍历LayoutInflater生成的View,把符合条件的View都找出来存在一个List数组中,代替原来的ViewHolder保存在 View的Tag中。
第二步:在设置控件的值的地方,遍历第一步中的控件数组,给每一个控件赋值。
代码分析
我的适配器继承自BaseAdapter (其实这个无所谓,ArrayAdapter也可以),构造函数中传入的数据类型换为 List<Map<String,Object>>。适配器中除getView外,别的都没有变化。下面我们来分析getView函数。
在这个函数
@Override
public View getView(int pos, View view, ViewGroup viewGroup) {
List<View> views=null;
if(view==null){
view = mLayoutInflater.inflate(R.layout.fragment_one_listview,viewGroup,false);
views=AdapterHelper.GetViewHolder(view);
view.setTag(views);
}else {
views=(List<View>)view.getTag();
}
AdapterHelper.SetViewValue(views,mMsgList.get(pos));
return view;
}
首先定义一个List views来代替原来的ViewHold,在第一次加载ListVew的item资料源时,我用GetViewHolder(view)遍历view中的所有符合条件的控件(前提条件就用上了),并记录在view的Tag中。查找符合条件的代码如下:
public static List<View> GetViewHolder(View view){
if (null == view ) {
return null;
}
List<View> views = new ArrayList<>();
if(!(view instanceof ViewGroup)){
views.add(view);
return views;
}
ViewGroup viewGroup = (ViewGroup)view;
LinkedList<ViewGroup> linkedList = new LinkedList<>();
linkedList.add(viewGroup);
while (!linkedList.isEmpty()) {
//removeFirst()删除第一个元素,并返回该元素
ViewGroup current = linkedList.removeFirst();
//遍历linkedList中第一个viewGroup中的子view
for (int i = 0; i < current.getChildCount(); i++) {
View currentView =current.getChildAt(i);
if (currentView instanceof ViewGroup) {
linkedList.addLast((ViewGroup) currentView);
} else {
if(isMustView(currentView)) {
views.add(current.getChildAt(i));
}
}
}
}
return views;
}
public static final String PREFIX_TEXTVIEW="txt";
public static final String PREFIX_IMGAGEVIEW="img";
public static final String PREFIX_RADIOBUTTON="rad";
private static Boolean isMustView(View view){
Resources resources=view.getResources();
int id=view.getId();
if(id==-1){
return false;
}
String strViewName=resources.getResourceEntryName(view.getId());
if(strViewName.startsWith(PREFIX_TEXTVIEW)) return true;
if(strViewName.startsWith(PREFIX_IMGAGEVIEW)) return true;
if(strViewName.startsWith(PREFIX_RADIOBUTTON)) return true;
return false;
}
接下来就是重头戏了,也就是给控件赋值。我们只用循环ListView数组给每个控件赋值就可以了。代码如下。
AdapterHelper.SetViewValue(views,mMsgList.get(pos));
SetViewValue函数的代码如下:
public static void SetViewValue(List<View> views, Map<String, Object> map) {
String strViewName="";
for (int i = 0; i < views.size(); i++) {
View view = views.get(i);
if(view.getId()==-1) continue;
strViewName = view.getResources().getResourceEntryName(view.getId());
strViewName = strViewName.substring(4);
if(view instanceof TextView) {
((TextView) view).setText(String.valueOf(map.get(strViewName)));
} else if (view instanceof ImageView) {
((ImageView) view).setImageBitmap((Bitmap)map.get(strViewName));
}else if( view instanceof RadioButton){
((RadioButton) view).setText(String.valueOf(map.get(strViewName)));
}
}
}
以下是我的这个文件全部代码:
class Adapter_ListView extends BaseAdapter {
private List<Map<String,Object>> mMsgList;
private LayoutInflater mLayoutInflater;
private Context mContext;
public Adapter_ListView(Context context,List<Map<String,Object>> msgList) {
this.mMsgList=msgList;
this.mContext=context;
mLayoutInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return mMsgList.size();
}
@Override
public Map<String,Object> getItem(int i) {
return mMsgList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int pos, View view, ViewGroup viewGroup) {
List<View> views=null;
if(view==null){
view = mLayoutInflater.inflate(R.layout.fragment_one_listview,viewGroup,false);
views=AdapterHelper.GetViewHolder(view);
view.setTag(views);
}else {
views=(List<View>)view.getTag();
}
AdapterHelper.SetViewValue(views,mMsgList.get(pos));
return view;
}
}
class AdapterHelper {
public static final String PREFIX_TEXTVIEW="txt";
public static final String PREFIX_IMGAGEVIEW="img";
public static final String PREFIX_RADIOBUTTON="rad";
public static List<View> GetViewHolder(View view){
if (null == view ) {
return null;
}
List<View> views = new ArrayList<>();
if(!(view instanceof ViewGroup)){
views.add(view);
return views;
}
ViewGroup viewGroup = (ViewGroup)view;
LinkedList<ViewGroup> linkedList = new LinkedList<>();
linkedList.add(viewGroup);
while (!linkedList.isEmpty()) {
//removeFirst()删除第一个元素,并返回该元素
ViewGroup current = linkedList.removeFirst();
//遍历linkedList中第一个viewGroup中的子view
for (int i = 0; i < current.getChildCount(); i++) {
View currentView =current.getChildAt(i);
if (currentView instanceof ViewGroup) {
linkedList.addLast((ViewGroup) currentView);
} else {
if(isMustView(currentView)) {
views.add(current.getChildAt(i));
}
}
}
}
return views;
}
private static Boolean isMustView(View view){
Resources resources=view.getResources();
int id=view.getId();
if(id==-1){
return false;
}
String strViewName=resources.getResourceEntryName(view.getId());
if(strViewName.startsWith(PREFIX_TEXTVIEW)) return true;
if(strViewName.startsWith(PREFIX_IMGAGEVIEW)) return true;
if(strViewName.startsWith(PREFIX_RADIOBUTTON)) return true;
return false;
}
public static void SetViewValue(List<View> views, Map<String, Object> map) {
String strViewName="";
for (int i = 0; i < views.size(); i++) {
View view = views.get(i);
if(view.getId()==-1) continue;
strViewName = view.getResources().getResourceEntryName(view.getId());
strViewName = strViewName.substring(4);
if(view instanceof TextView) {
((TextView) view).setText(String.valueOf(map.get(strViewName)));
} else if (view instanceof ImageView) {
((ImageView) view).setImageBitmap((Bitmap)map.get(strViewName));
}else if( view instanceof RadioButton){
((RadioButton) view).setText(String.valueOf(map.get(strViewName)));
}
}
}
}