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隐藏,才能保证数据操作是可充用可拓展的。最后上一张完成图,感谢韩MM提供切图~








































































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值