看到一篇相当不错的博客,记录了如何将xml动态加载到Gridview中,实现动态加载
在此记录一下:
Android根据xml配置文件动态修改九宫格功能配置
再次记录一下,防止走丢。
大家知道很多app的首页都是由GridView组成的功能入口,这个九宫格一般在第一版的时候把功能写死的,这显然不能适应需求的不断变更的,所以今天在这里想写一个简单的根据配置文件自动生成的gridView,点击之后可跳转到配置好的Activity而不用修改一行代码。
首先设计一个简单的xml配置文件,塞到res/xml文件夹下,当然这里后期可以配置到服务器上动态获取更新,demo的话就先放在apk里了。
<?xml version="1.0" encoding="utf-8"?>
<Functions>
<Function
name="pay"
displayName="功能1"
icon="icon_1"
packageName="com.suning.epa_plugin.pay"
activityName="PayActivity"
/>
<Function
name="assets"
displayName="功能2"
icon="icon_2"
packageName="com.suning.epa_plugin.assets"
activityName="AssetsActivity"
/>
<Function
name="assets"
displayName="功能3"
icon="icon_3"
packageName="com.suning.epa_plugin.assets"
activityName="AssetsActivity"
/>
<Function
name="assets"
displayName="功能4"
icon="icon_4"
packageName="com.suning.epa_plugin.assets"
activityName="AssetsActivity"
/>
<Function
name="assets"
displayName="功能5"
icon="icon_5"
packageName="com.suning.epa_plugin.assets"
activityName="AssetsActivity"
/>
<Function
name="assets"
displayName="功能6"
icon="icon_6"
packageName="com.suning.epa_plugin.assets"
activityName="AssetsActivity"
/>
<Function
name="assets"
displayName="功能7"
icon="icon_7"
packageName="com.suning.epa_plugin.assets"
activityName="AssetsActivity"
/>
<Function
name="assets"
displayName="功能8"
icon="icon_8"
packageName="com.suning.epa_plugin.assets"
activityName="AssetsActivity"
/>
</Functions>
为了简单就改了displayName和icon,其他都一样了,实际使用的时候可根据需求来配置自己想要的Activity。
接下来写一个bean类来封装xml中的每一个item,这个很简单,不多说。
package com.suning.functions;
import android.os.Parcel;
import android.os.Parcelable;
public class FunctionBean implements Parcelable
{
private String name;
private String packageName;
private String activityName;
private String displayName;
private String icon;
public FunctionBean()
{
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getPackageName()
{
return packageName;
}
public void setPackageName(String packageName)
{
this.packageName = packageName;
}
public String getActivityName()
{
return activityName;
}
public void setActivityName(String activityName)
{
this.activityName = activityName;
}
public String getDisplayName()
{
return displayName;
}
public void setDisplayName(String displayName)
{
this.displayName = displayName;
}
public String getIcon()
{
return icon;
}
public void setIcon(String icon)
{
this.icon = icon;
}
@Override
public int describeContents()
{
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags)
{
dest.writeString(name);
dest.writeString(packageName);
dest.writeString(activityName);
dest.writeString(displayName);
dest.writeString(icon);
}
public static final Creator<FunctionBean> CREATOR = new Creator<FunctionBean>()
{
public FunctionBean createFromParcel(Parcel in)
{
return new FunctionBean(in);
}
public FunctionBean[] newArray(int size)
{
return new FunctionBean[size];
}
};
private FunctionBean(Parcel in)
{
name = in.readString();
packageName = in.readString();
activityName = in.readString();
displayName = in.readString();
icon = in.readString();
}
}
有了xml和数据的封装,接下来就要解析了,为了xml文件的拓展,这里用反射来自动为bean设定值,当xml字段增加的时候,解析器类不需要修改代码:
package com.suning.functions;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import com.amuro.xmlparsertest.R;
import android.content.Context;
import android.content.res.XmlResourceParser;
public class FunctionXMLReader<T>
{
private Context context;
private Class<T> classT;
private XmlResourceParser xmlParser;
private List<T> beans;
public FunctionXMLReader(Context context, Class<T> classT)
{
this.context = context;
this.classT = classT;
init();
}
private void init()
{
xmlParser = context.getResources().getXml(R.xml.functions);
beans = new ArrayList<T>();
}
public void parse()
{
try
{
int eventType = xmlParser.getEventType();
while(eventType != XmlPullParser.END_DOCUMENT)
{
switch(eventType)
{
case XmlPullParser.START_DOCUMENT:
break;
case XmlPullParser.START_TAG:
parseTags();
break;
case XmlPullParser.END_TAG:
break;
case XmlPullParser.END_DOCUMENT:
break;
}
eventType = xmlParser.next();
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
private void parseTags() throws InstantiationException, IllegalAccessException
{
String tagName = xmlParser.getName();
if("Function".equals(tagName))
{
T bean = classT.newInstance();
Field[] fields = classT.getDeclaredFields();
for(int i = 0;i < xmlParser.getAttributeCount();i++)
{
String attrName = xmlParser.getAttributeName(i);
String attrValue = xmlParser.getAttributeValue(i);
for(Field f : fields)
{
f.setAccessible(true);
String fName = f.getName();
if(fName.equals(attrName))
{
f.set(bean, attrValue);
}
}
}
beans.add(bean);
}
}
public List<T> getBeans()
{
return beans;
}
}
用泛型的原因是为了进一步拓展,以后需要解析其他xml文件时,拓展这个类就可以了,暂时还没有全部完成,不过用来做九宫格是没问题了。好了,接下来就是对上层Activity封装xml解析,提供跳转activity等功能。
package com.suning.functions;
import java.util.List;
import android.content.Context;
import android.content.Intent;
public class FunctionManager
{
private static volatile FunctionManager instance = null;
private FunctionManager(Context context)
{
this.context = context;
}
public static FunctionManager getInstance(Context context)
{
if(instance == null)
{
synchronized (FunctionManager.class)
{
if(instance == null)
{
instance = new FunctionManager(context);
}
}
}
return instance;
}
private Context context;
private FunctionXMLReader<FunctionBean> xmlReader;
private List<FunctionBean> beans;
public void init()
{
xmlReader = new FunctionXMLReader<FunctionBean>(context, FunctionBean.class);
xmlReader.parse();
beans = xmlReader.getBeans();
}
public List<FunctionBean> getBeans()
{
return beans;
}
public void lauchFunction(int position) throws ClassNotFoundException
{
FunctionBean bean = beans.get(position);
String className = bean.getPackageName() + "." + bean.getActivityName();
Intent intent = new Intent();
intent.setClass(context, Class.forName(className));
context.startActivity(intent);
}
}
因为bean list生成时的顺序正好对应九宫格的顺序,所以上层的调用将变得非常轻松愉快,老规矩写一个adapter配给GridView就可以了,先看Adapter:
package com.suning.test;
import java.lang.reflect.Field;
import java.util.List;
import com.amuro.xmlparsertest.R;
import com.suning.functions.FunctionBean;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class GridAdapter extends ArrayAdapter<FunctionBean>
{
private LayoutInflater layoutInflater;
public GridAdapter(Context context, List<FunctionBean> beans)
{
super(context, 0, beans);
layoutInflater = LayoutInflater.from(context);
}
@SuppressLint("InflateParams")
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
FunctionBean bean = getItem(position);
ViewHolder viewHolder = null;
if(convertView == null)
{
convertView = layoutInflater.inflate(R.layout.adapter_grid_layout, null);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
}
else
{
viewHolder = (ViewHolder)convertView.getTag();
}
viewHolder.textViewName.setText(bean.getDisplayName());
viewHolder.imageViewIcon.setImageResource(getResourceId(bean.getIcon()));
return convertView;
}
private int getResourceId(String iconName)
{
Field field;
int resId = R.drawable.ic_launcher;
try
{
field = R.drawable.class.getField(iconName);
resId = (int) field.get(null);
}
catch (NoSuchFieldException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
return resId;
}
static class ViewHolder
{
private ImageView imageViewIcon;
private TextView textViewName;
public ViewHolder(View convertView)
{
imageViewIcon = (ImageView)convertView.findViewById(R.id.iv_icon);
textViewName = (TextView)convertView.findViewById(R.id.tv_display_name);
}
}
}
注意这里的getResourseId方法,因为xml文件里配置的是icon的名称,是一个string,需要把这个string转换成R类里对应的id,又要用到反射,这个小技巧大家可以参考下。真实的项目中,这里的icon应该是一个url,用ImageLoader来加载。好了,下面就是轻松愉快地写个Activity来调用上面这些类来服务了~
package com.suning.test;
import com.amuro.xmlparsertest.R;
import com.suning.functions.FunctionManager;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.GridView;
public class TestActivity extends Activity
{
private FunctionManager fManager;
private GridView gridView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_layout);
initFunctionManager();
initView();
}
private void initFunctionManager()
{
fManager = FunctionManager.getInstance(this);
fManager.init();
}
private void initView()
{
findViewById(R.id.bt).setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
}
});
gridView = (GridView) findViewById(R.id.gv);
gridView.setAdapter(new GridAdapter(this, fManager.getBeans()));
gridView.setOnItemClickListener(new OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
{
lauchActivity(position);
}
});
}
private void lauchActivity(int position)
{
try
{
fManager.lauchFunction(position);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
}
界面的xml自己写
是不是轻松愉快,本人的理念就是要让Activity就只做一个控件的控制器,其他所有数据相关的细节,都应该对Activity隐藏,才能保证数据操作是可充用可拓展的。