一步步实现 仿制Android LOL多玩盒子(二) 物品装备相关

一、原应用相关模块简介

1,入口:主界面-----游戏百科-----装备。
2,装备物品主界面中,列表选择物品类型,进入指定物品类型GridView,GridView点击某一项进入指定物品的物品详情界面。物品详情界面主要内容有:物品名称、物品价格、物品缩略图、物品属性、合成该物品所需物品,该物品可合成的物品。


二、功能分析

1,物品分类页面是一个ListView且分类是固定的;分类下装备列表是一个GridView,一共四列,每个物品包含一个图片和一个图片名称;物品详情界面包含一个ImageView,几个TextView和两个HorizontalScrollView,两个HorizontalScrollView中分别包含当前物品的合成所需和可合成物品的图标,图标点击后可进入到响应物品的物品详情界面。

2,获取某一物品分类下的所有物品的简要信息,使用 fiddler 抓包发现,访问请求格式为 http://lolbox.duowan.com/phone/apiZBItemList.php?tag=fumo ,其中tag参数的值代表物品分类,其返回值是一个Json,格式为
[{"id":3250,"text":"\u72c2\u6218\u58eb\u80eb\u7532\u2014\u5bb6\u56ed\u536b\u58eb"},{"id":3251,"text":"\u72c2\u6218\u58eb\u80eb\u7532\u2014\u7edf\u5e05"},{"id":3252,"text":"\u72c2\u6218\u58eb\u80eb\u7532\u2014\u55a7\u54d7"},{"id":3253,"text":"\u72c2\u6218\u58eb\u80eb\u7532\u2014\u5931\u771f"},{"id":3254,"text":"\u72c2\u6218\u58eb\u80eb\u7532\u2014\u6b22\u6b23"},{"id":3255,"text":"\u6cd5\u5e08\u4e4b\u9774\u2014\u5bb6\u56ed\u536b\u58eb"},{"id":3256,"text":"\u6cd5\u5e08\u4e4b\u9774\u2014\u7edf\u5e05"},{"id":3257,"text":"\u6cd5\u5e08\u4e4b\u9774\u2014\u55a7\u54d7"},{"id":3258,"text":"\u6cd5\u5e08\u4e4b\u9774\u2014\u5931\u771f"},{"id":3259,"text":"\u6cd5\u5e08\u4e4b\u9774\u2014\u6b22\u6b23"},{"id":3260,"text":"\u5fcd\u8005\u8db3\u5177\u2014\u5bb6\u56ed\u536b\u58eb"},{"id":3261,"text":"\u5fcd\u8005\u8db3\u5177\u2014\u7edf\u5e05"},{"id":3262,"text":"\u5fcd\u8005\u8db3\u5177\u2014\u55a7\u54d7"},{"id":3263,"text":"\u5fcd\u8005\u8db3\u5177\u2014\u5931\u771f"},{"id":3264,"text":"\u5fcd\u8005\u8db3\u5177\u2014\u6b22\u6b23"},{"id":3265,"text":"\u6c34\u94f6\u4e4b\u9774\u2014\u5bb6\u56ed\u536b\u58eb"},{"id":3266,"text":"\u6c34\u94f6\u4e4b\u9774\u2014\u7edf\u5e05"},{"id":3267,"text":"\u6c34\u94f6\u4e4b\u9774\u2014\u55a7\u54d7"},{"id":3268,"text":"\u6c34\u94f6\u4e4b\u9774\u2014\u5931\u771f"},{"id":3269,"text":"\u6c34\u94f6\u4e4b\u9774\u2014\u6b22\u6b23"},{"id":3270,"text":"\u75be\u884c\u4e4b\u9774\u2014\u5bb6\u56ed\u536b\u58eb"},{"id":3271,"text":"\u75be\u884c\u4e4b\u9774\u2014\u7edf\u5e05"},{"id":3272,"text":"\u75be\u884c\u4e4b\u9774\u2014\u55a7\u54d7"},{"id":3273,"text":"\u75be\u884c\u4e4b\u9774\u2014\u5931\u771f"},{"id":3274,"text":"\u75be\u884c\u4e4b\u9774\u2014\u6b22\u6b23"},{"id":3275,"text":"\u660e\u6717\u4e4b\u9774\u2014\u5bb6\u56ed\u536b\u58eb"},{"id":3276,"text":"\u660e\u6717\u4e4b\u9774\u2014\u7edf\u5e05"},{"id":3277,"text":"\u660e\u6717\u4e4b\u9774\u2014\u55a7\u54d7"},{"id":3278,"text":"\u660e\u6717\u4e4b\u9774\u2014\u5931\u771f"},{"id":3279,"text":"\u660e\u6717\u4e4b\u9774\u2014\u6b22\u6b23"},{"id":3280,"text":"\u8f7b\u7075\u4e4b\u9774\u2014\u5bb6\u56ed\u536b\u58eb"},{"id":3281,"text":"\u8f7b\u7075\u4e4b\u9774\u2014\u7edf\u5e05"},{"id":3282,"text":"\u8f7b\u7075\u4e4b\u9774\u2014\u55a7\u54d7"},{"id":3283,"text":"\u8f7b\u7075\u4e4b\u9774\u2014\u5931\u771f"},{"id":3284,"text":"\u8f7b\u7075\u4e4b\u9774\u2014\u6b22\u6b23"}]
结果只包含物品ID和物品名称,继续查看抓包信息可知, 由物品id取物品缩略图的 请求格式为 http://img.lolbox.duowan.com/zb/3250_64x64.png ,最后一部分下划线前是物品id,下划线后是要取的图片的大小。至此,获取指定分类的物品列表的功能所需条件已经完备。

3,获取某一物品的详细信息的请求格式为 http://lolbox.duowan.com/phone/apiItemDetail.php?id=3093,其中id参数的值代表物品的ID,请求结果是Json,格式为
{"id":"3093","name":"\u8d2a\u5a6a\u4e4b\u5203","description":"+10%\u66b4\u51fb\u51e0\u7387\uff0c\u552f\u4e00\u88ab\u52a8\u2014\u8d2a\u8d22\uff1a\u6bcf10\u79d2\u83b7\u5f97+3\u679a\u91d1\u5e01\u3002\u552f\u4e00\u88ab\u52a8\u2014\u8d2a\u5a6a\uff1a\u6bcf\u6b21\u51fb\u6740\u5355\u4f4d\u65f6\u989d\u5916\u83b7\u5f972\u679a\u91d1\u5e01\u3002\u53ef\u4ee5\u4e0e\u5176\u5b83\u91d1\u5e01\u6536\u5165\u578b\u88c5\u5907\u5171\u5b58","price":400,"allPrice":800,"sellPrice":320,"tags":" ","extAttrs":{"FlatCritChanceMod":0.1},"need":"1051","compose":"3087,3142","extDesc":"\u552f\u4e00\u88ab\u52a8\u2014\u8d2a\u8d22\uff1a\u6bcf10\u79d2\u83b7\u5f97+3\u679a\u91d1\u5e01\u3002\u552f\u4e00\u88ab\u52a8\u2014\u8d2a\u5a6a\uff1a\u6bcf\u6b21\u51fb\u6740\u5355\u4f4d\u65f6\u989d\u5916\u83b7\u5f972\u679a\u91d1\u5e01\u3002\u53ef\u4ee5\u4e0e\u5176\u5b83\u91d1\u5e01\u6536\u5165\u578b\u88c5\u5907\u5171\u5b58\u3002"}
结果中包含了当前物品 的ID、名称、价格、属性、合成所需物品的ID、可合成物品的ID。依据这些属性再结合前面根据ID获取物品图片的方式,即可把物品详情界面中的元素完全展示出来。

三、功能实现

1,几个工具类
/**
 * 路径工具
 * @author yangsheng
 * @date 2014年12月30日
 */
public class URLUtil {
	
	/**
	 * 取某分类的装备列表Json的请求URL
	 * @param type
	 * @return
	 */
	public static final String getURL_ZBLst(EnumZBType type){
		return String.format("http://lolbox.duowan.com/phone/apiZBItemList.php?tag=%s", type.toString());
	}
	
	
	/**
	 * 取装备图片的请求URL
	 * @param zbId 装备id
	 * @param dpi  图片分辨率
	 * @return
	 */
	public static final String getURL_ZBImg(int zbId, EnumDPI dpi){
		return String.format("http://img.lolbox.duowan.com/zb/%s_%s.png", zbId, dpi.toDPIString());
	}
	
	/**
	 * 取装备详细信息Json的请求URL
	 * @param zbId
	 * @return
	 */
	public static final String getURL_ZBDetail(int zbId){
		return String.format("http://lolbox.duowan.com/phone/apiItemDetail.php?id=%s", zbId);
	}
	
}
将网络请求操作统一封装在一个管理器中,方便统一管理
/**
 * 全局的网络请求管理器
 * @author warren
 * @date 2014年12月30日
 */
public class AppNetManager {
	
	private static AppNetManager netManager;
	
	private LruCache<String, String> cache = new LruCache<String, String>(1000);

	/**
	 * 线程安全地取唯一实例
	 * @return
	 */
	public static AppNetManager getInstance(){
		
		if(netManager == null){
			synchronized (AppNetManager.class) {
				if(netManager == null){
					netManager = new AppNetManager();
				}
			}
		}
		return netManager;
	}
	
	private AppNetManager(){
		
	}
	
	/**
	 * 异步请求指定URL,同步回调
	 * @param strUrl
	 * @param listener
	 */
	public void get(final String strUrl, final IListener<String> listener){
		
		String strHistory = cache.get(strUrl);
		if(strHistory != null){
			listener.onCall(strHistory);
			return;
		}
		
		AsyncHttpClient httpClient = new AsyncHttpClient();
		httpClient.get(strUrl, new AsyncHttpResponseHandler(){
			
			@Override
			public void onSuccess(int arg0, Header[] arg1, byte[] arg2) {
				super.onSuccess(arg0, arg1, arg2);
				try {
					String strResult = new String(arg2, "UTF-8");
					cache.put(strUrl, strResult);
					listener.onCall(strResult);
					
				} catch (UnsupportedEncodingException e) {
					LogTool.exception(e);
					listener.onCall(null);
				}
			}
			
			@Override
			public void onFailure(int arg0, Header[] arg1, byte[] arg2, Throwable arg3) {
				super.onFailure(arg0, arg1, arg2, arg3);
				listener.onCall(null);
			}
			
		});
	}

Json比较大时,解析操作是比较耗时的,这里把解析操作封装为异步解析,同步回调。
/**
 * Json解析管理器
 * @author warren
 * @date 2014年12月31日
 */
public class AppJsonParserManager {

	private static AppJsonParserManager jpm;

	public static AppJsonParserManager getInstance() {

		if (jpm == null) {
			synchronized (AppJsonParserManager.class) {
				if (jpm == null) {
					jpm = new AppJsonParserManager();
				}
			}
		}
		return jpm;
	}

	private AppJsonParserManager() {

	}

	/**
	 * 异步解析Json,同步回调。解析成功则回调方法中的参数是解析结果,否则是 null
	 * @param strJson
	 * @param cls
	 * @param listener
	 */
	public <T> void parse(final String strJson, final Class<T> cls, final IListener<T> listener) {

		new AsyncTaskParser<T>(listener, cls).execute(strJson);
	}

	/**
	 * 异步解析列表Json,同步回调。解析成功则回调方法中的参数是解析结果,否则是 null。
	 * @param strJson
	 * @param cls
	 * @param listener
	 */
	public <T> void parseList(final String strJson, final Class<T> cls,
				final IListener<List<T>> listener) {

		new AsyncTaskParserList<T>(cls, listener).execute(strJson);
	}

	/**
	 * 异步解析列表Json,同步回调
	 * @author warren
	 * @date 2014年12月31日
	 * @param <T>
	 */
	class AsyncTaskParserList<T> extends AsyncTask<String, Integer, List<T>> {

		private Class<T> cls;
		private IListener<List<T>> listener;

		public AsyncTaskParserList(Class<T> cls, IListener<List<T>> listener) {
			this.listener = listener;
			this.cls = cls;
		}

		@Override
		protected List<T> doInBackground(String... params) {

			StringReader sr = new StringReader(params[0]);

			List<T> lst = new ArrayList<T>();
			try {
				sr.reset();
				// 解析成List时,无法直接解析成指定类型的列表,所以需要把解析得到的List<HashMap<String,
				// Object>>每一项单独再转成指定类型的对象。
				JsonFactory factory = new ObjectMapper().getJsonFactory();
				JsonParser jpar = factory.createJsonParser(sr);
				List<HashMap<String, Object>> mapLst = jpar.readValueAs(List.class);
				for (HashMap<String, Object> map : mapLst) {

					// 先将Map转换为Json字符串,再把Json字符串重新转换为指定类型的对象。
					StringWriter sw = new StringWriter();
					factory.createJsonGenerator(sw).writeObject(map);
					T t = factory.createJsonParser(sw.toString()).readValueAs(cls);
					lst.add(t);
					sw.close();
				}

			} catch (Exception e) {
				LogTool.exception(e);
			}
			sr.close();
			return lst;
		}

		@Override
		protected void onPostExecute(List<T> result) {
			super.onPostExecute(result);
			listener.onCall(result);
		}

	}

	/**
	 * 异步解析Json,同步回调
	 * @author warren
	 * @date 2014年12月31日
	 * @param <T>
	 */
	class AsyncTaskParser<T> extends AsyncTask<String, Integer, T> {

		private IListener<T> listener;;
		private Class<T> cls;

		public AsyncTaskParser(IListener<T> listener, Class<T> cls) {
			this.listener = listener;
			this.cls = cls;
		}

		@Override
		protected T doInBackground(String... params) {

			StringReader sr = new StringReader(params[0]);
			T obj = null;
			try {
				JsonParser jpar = new ObjectMapper().getJsonFactory().createJsonParser(sr);
				obj = jpar.readValueAs(cls);

			} catch (Exception e) {
				LogTool.exception(e);
			}
			sr.close();
			return obj;
		}

		@Override
		protected void onPostExecute(T result) {
			super.onPostExecute(result);
			listener.onCall(result);
		}

	}

GridView的通用适配器
/**
 * 通用的GridView的Adapter,支持 {@link SimpleTool} 和 {@link SimpleNetTool}列表的显示
 * @author yangsheng
 * @date 2014年12月31日
 */
public class BaseGridAdapter extends BaseAdapter {

	private LayoutInflater inflater;
	private List<SimpleTool> lstTools;
	private List<SimpleNetTool> lstNetTools;
	private ImageLoader imgLoader;
	private DisplayImageOptions options;

	private int numColumn = 3;

	public BaseGridAdapter(LayoutInflater inflater) {
		this.inflater = inflater;
	}

	public void setLstTools(List<SimpleTool> lstTools) {
		this.lstTools = lstTools;
	}

	public void setLstNetTools(List<SimpleNetTool> lstNetTools, ImageLoader imgLoader, DisplayImageOptions options) {
		this.lstNetTools = lstNetTools;
		this.imgLoader = imgLoader;
		this.options = options;
	}

	/**
	 * @return the numColumn
	 */
	public int getNumColumn() {
		return numColumn;
	}

	/**
	 * @param numColumn the numColumn to set
	 */
	public void setNumColumn(int numColumn) {
		this.numColumn = numColumn;
	}

	@Override
	public int getCount() {
		return lstTools != null ? lstTools.size() : (lstNetTools != null ? lstNetTools.size() : 0);
	}

	@Override
	public Object getItem(int position) {
		return null;
	}

	@Override
	public long getItemId(int position) {
		return 0;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {

		ViewHolder holder;
		if (convertView == null) {
			convertView = inflater.inflate(R.layout.griditem, parent, false);
			holder = new ViewHolder();
			holder.img = (ImageView) convertView.findViewById(R.id.img);
			holder.tv = (TextView) convertView.findViewById(R.id.tv);
			convertView.setTag(holder);

		} else {
			holder = (ViewHolder) convertView.getTag();
		}

		// 优先显示 lstTools,如果 lstTools 为设置,则显示 lstNetTools
		if (lstTools != null) {
			holder.img.setImageResource(lstTools.get(position).getImgResId());
			holder.tv.setText(lstTools.get(position).getTxtResId());

		} else {
			imgLoader.displayImage(lstNetTools.get(position).getStrImgUrl(), holder.img, options);
			holder.tv.setText(lstNetTools.get(position).getStrText());
		}

		AbsListView.LayoutParams param = new AbsListView.LayoutParams(
					parent.getWidth() / numColumn, android.view.ViewGroup.LayoutParams.MATCH_PARENT);
		convertView.setLayoutParams(param);

		return convertView;
	}

	class ViewHolder {
		ImageView img;
		TextView tv;
	}

}
物品分类界面
/**
 * 物品分类
 * @author warren
 * @date 2014年12月31日
 */
public class MaterialTypesActivity extends Activity {

	private ImageView mImgLeft;
	private ImageView mImgRight;
	private TextView mTvTitle;
	private ListView mLvLst;

	private String[] mArrTypes = EnumZBType.getStringTypeArray();
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_baike);
		initCtrl();
	}

	private void initCtrl() {

		mImgLeft = (ImageView) findViewById(R.id.img_title_left);
		mImgRight = (ImageView) findViewById(R.id.img_title_right);
		mTvTitle = (TextView) findViewById(R.id.tv_title);

		mImgLeft.setImageResource(R.drawable.lolbox_titleview_return_default);
		mImgRight.setVisibility(View.GONE);
		mTvTitle.setText("物品分类");

		mLvLst = (ListView) findViewById(R.id.lv_types);

		AdapterList adapter = new AdapterList(LayoutInflater.from(this), mArrTypes);

		mLvLst.setAdapter(adapter);
		mLvLst.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

				openNextActivity(position);
			}
		});
		mImgLeft.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				finish();
			}
		});
	}

	/**
	 * 打开物品列表Activity
	 * @param position
	 */
	private void openNextActivity(int position) {

		Intent it = new Intent(this, MaterialGridWithTypeActivity.class);
		
		it.putExtra(MaterialGridWithTypeActivity.EXTRA_ZBTYPE, mArrTypes[position]);
		startActivity(it);
		overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
	}

	class AdapterList extends BaseAdapter {

		private LayoutInflater mInflater;
		private String[] mArrTypes;
		
		public AdapterList(LayoutInflater inflater,  String[] arrType) {
			this.mInflater = inflater;
			this.mArrTypes = arrType;
		}

		@Override
		public int getCount() {
			return mArrTypes.length;
		}

		@Override
		public Object getItem(int position) {
			return mArrTypes[position];
		}

		@Override
		public long getItemId(int position) {
			return 0;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {

			ViewHolder holder;
			if (convertView == null) {
				convertView = mInflater.inflate(R.layout.activity_baike_listitem, parent, false);
				holder = new ViewHolder();
				holder.img = (ImageView) convertView.findViewById(R.id.img);
				holder.tv = (TextView) convertView.findViewById(R.id.tv);
				convertView.setTag(holder);
			} else {
				holder = (ViewHolder) convertView.getTag();
			}
			holder.img.setVisibility(View.GONE);
			holder.tv.setText(mArrTypes[position]);
			return convertView;
		}

		class ViewHolder {
			public ImageView img;
			public TextView tv;
		}

	}
}

特定分类下的物品列表

/**
 * 特定分类下的物品列表
 * @author warren
 * @date 2014年12月31日
 */
public class MaterialGridWithTypeActivity extends Activity {

	public static final String EXTRA_ZBTYPE = "EXTRA_ZBTYPE";

	private ImageView mImgLeft;
	private ImageView mImgRight;
	private TextView mTvTitle;

	private GridView mGv;

	private String mStrTitle;
	private EnumZBType mEnumType;

	private BaseGridAdapter mAdapter;
	private List<MaterialSimple> mLstMs;
	private List<SimpleNetTool> mLstSnt;

	private DisplayImageOptions mDisPlayOption;

	@Override
	protected void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_grid);
		String strType = getIntent().getStringExtra(EXTRA_ZBTYPE);
		if (StringUtils.isNullOrZero(strType)) {
			mStrTitle = "全部装备";
			mEnumType = EnumZBType.all;
		} else {
			mStrTitle = strType;
			mEnumType = EnumZBType.getZbType(StringUtils.getIndex(EnumZBType.getStringTypeArray(),
						strType));
		}
		initImgOption();
		initCtrl();
	}

	private void initImgOption() {

		// 这里无需设置这么多,一般使用ImageLoader的默认DisplayImageOptions就可以满足需求
		Options opt = new Options();
		opt.inInputShareable = true;
		opt.inPurgeable = true;
		opt.inPreferredConfig = Bitmap.Config.RGB_565;
		opt.inSampleSize = 1;

		mDisPlayOption = new DisplayImageOptions.Builder()
					.considerExifParams(true)
					.bitmapConfig(Config.RGB_565)
					.decodingOptions(opt)
					.displayer(new BitmapDisplayer() {

						@Override
						public void display(Bitmap arg0, ImageAware arg1, LoadedFrom arg2) {
							
							// 由于请求的图片较小,在分辨率较大的设备上显示不美观。这里在显示前先放大到两倍,再显示。
							// 放大到两倍后,经检查大部分手机分辨率都能较好地展示,而放大后的图片对某一分辨率来说太大时,
							// 可通过设置ImageView的ScaleType来将自动将图片缩小。
							
							Matrix matrix = new Matrix();
							matrix.postScale(2, 2); 
							Bitmap bitResize = Bitmap.createBitmap(arg0, 0, 0, arg0.getWidth(),
										arg0.getHeight(), matrix, true);
							arg1.setImageBitmap(bitResize);
						}
					}).cacheInMemory(true).showImageOnLoading(R.drawable.dl_loading_img)
					.imageScaleType(ImageScaleType.IN_SAMPLE_INT).build();
	}

	private void initCtrl() {

		mImgLeft = (ImageView) findViewById(R.id.img_title_left);
		mImgRight = (ImageView) findViewById(R.id.img_title_right);
		mTvTitle = (TextView) findViewById(R.id.tv_title);

		mImgLeft.setImageResource(R.drawable.lolbox_titleview_return_default);
		mImgRight.setVisibility(View.GONE);
		mTvTitle.setText(mStrTitle);

		mImgLeft.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				finish();
			}
		});
		
		mGv = (GridView) findViewById(R.id.grid);
		mGv.setNumColumns(4);

		mAdapter = new BaseGridAdapter(LayoutInflater.from(this));

		GridParams params = GridParams.createNormal();
		params.imgFillRootWidth = true;
		mAdapter.setNumColumn(4);
		mGv.setAdapter(mAdapter);

		// 请求服务器,查询某一类型的物品列表
		AppContext.getApp().getNetManager()
					.get(URLUtil.getURL_ZBLst(mEnumType), new IListener<String>() {

						@Override
						public void onCall(String t) {
							
							if (t == null) {
								Toast.makeText(MaterialGridWithTypeActivity.this, "没有符合条件的物品",
											Toast.LENGTH_SHORT).show();
								return;
							}
							
							// 获得物品列表Json后,转换为所需要的对象列表
							AppContext.getApp().getJsonManager().parseList(t, MaterialSimple.class,
										new IListener<List<MaterialSimple>>() {

											@Override
											public void onCall(List<MaterialSimple> lstMs) {

												if(lstMs == null || lstMs.size() == 0){
													Toast.makeText(MaterialGridWithTypeActivity.this, "没有符合条件的物品",
																Toast.LENGTH_SHORT).show();
													return;
												}
												mLstMs = lstMs;
												// 依据物品id,构造物品的图片地址,在Adapter中将根据该地址获取对应的图片
												mLstSnt = new ArrayList<SimpleNetTool>();
												for (MaterialSimple ms : lstMs) {
													SimpleNetTool snt = new SimpleNetTool(URLUtil
																.getURL_ZBImg(ms.getId(),
																			EnumDPI.DPI64x64), ms
																.getText());
													mLstSnt.add(snt);
												}
												mAdapter.setLstNetTools(mLstSnt, AppContext
															.getApp().getImgLoader(), mDisPlayOption);
												mAdapter.notifyDataSetChanged();
											}

										});
						}
					});
		
		
		mGv.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
				openMaterialDetail("" + mLstMs.get(position).getId());
			}
		});

		
	}
	
	/**
	 * 打开物品详情界面
	 * @param materialId
	 */
	private void openMaterialDetail(String materialId) {

		Intent it = new Intent(this, MaterialDetailActivity.class);
		it.putExtra(MaterialDetailActivity.EXTRA_MATERIALID, materialId);
		startActivity(it);
		overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
	}
	

}

物品详情界面
/**
 * 物品详情
 * @author warren
 * @date 2014年12月31日
 */
public class MaterialDetailActivity extends Activity {

	public static final String EXTRA_MATERIALID = "EXTRA_MATERIALID";

	private ImageView mImgLeft;
	private ImageView mImgRight;
	private TextView mTvTitle;

	private LinearLayout mLlRoot;
	private ImageView mImgMaterial;
	private TextView mTvMaterialName;
	private TextView mTvMaterialPrice;
	private TextView mTvDescription;
	private LinearLayout mLlNeed;
	private LinearLayout mLlCompose;

	private ImageLoader mImgLoader;

	private String mStrMaterialId;

	private Material mMaterial;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_materialdetail);

		mImgLoader = AppContext.getApp().getImgLoader();

		mStrMaterialId = getIntent().getStringExtra(EXTRA_MATERIALID);

		initCtrl();
	}

	private void initCtrl() {

		mImgLeft = (ImageView) findViewById(R.id.img_title_left);
		mImgRight = (ImageView) findViewById(R.id.img_title_right);
		mTvTitle = (TextView) findViewById(R.id.tv_title);

		mImgLeft.setImageResource(R.drawable.lolbox_titleview_return_default);
		mImgRight.setVisibility(View.GONE);
		mTvTitle.setText("物品详情");
		mImgLeft.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				finish();
			}
		});

		mLlRoot = (LinearLayout) findViewById(R.id.ll_root);
		mLlNeed = (LinearLayout) findViewById(R.id.ll_need);
		mLlCompose = (LinearLayout) findViewById(R.id.ll_compose);

		mImgMaterial = (ImageView) findViewById(R.id.img_material);
		mTvMaterialName = (TextView) findViewById(R.id.tv_materialname);
		mTvMaterialPrice = (TextView) findViewById(R.id.tv_materialmoney);
		mTvDescription = (TextView) findViewById(R.id.tv_materialdescription);

		if (mStrMaterialId == null) {
			setNullHint();
			return;
		}

		// 请求服务器获取物品详情Json
		AppContext.getApp()
					.getNetManager()
					.get(URLUtil.getURL_ZBDetail(Integer.parseInt(mStrMaterialId)),
								new IListener<String>() {

									@Override
									public void onCall(String strJson) {

										strJson = checkMaterialJson(strJson);

										// 获取到Json后解析成 Material实体
										AppContext.getApp()
													.getJsonManager()
													.parse(strJson, Material.class,
																new IListener<Material>() {

																	@Override
																	public void onCall(
																				Material material) {

																		if (material == null) {
																			setNullHint();
																			return;
																		}
																		mMaterial = material;
																		setView();
																	}

																});

									}
								});

	}

	/**
	 * 纠正服务器传回的Json。
	 * @description
	 *              请求生命药水、法力药水等“没有需求物品也没有合成物品”的工具类物品的物品详情时,服务器返回的Json中,
	 *              extAtts的值是 "extAttrs":[] 格式,这与Material
	 *              类的extAttrs所需求的Map格式不符,需矫正,否则会解析失败。
	 * @param strJson
	 * @return
	 */
	private String checkMaterialJson(String strJson) {

		if (strJson.contains("\"extAttrs\":[]")) {
			strJson = strJson.replace("\"extAttrs\":[]", "\"extAttrs\":{}");
		}
		return strJson;
	}

	/**
	 * 依据物品详情设置界面
	 */
	private void setView() {

		mTvDescription.setText(mMaterial.getDescription());
		mTvMaterialName.setText(mMaterial.getName());
		mTvMaterialPrice.setText("价格:" + mMaterial.getPrice() + " 总价:" + mMaterial.getAllPrice()
					+ " 售价:" + mMaterial.getSellPrice());

		mImgLoader.displayImage(
					URLUtil.getURL_ZBImg(Integer.parseInt(mMaterial.getId()), EnumDPI.DPI64x64),
					mImgMaterial);

		String[] arrNeedId = mMaterial.getNeed() == null ? null : mMaterial.getNeed().split(",");
		String[] arrComposeId = mMaterial.getCompose() == null ? null : mMaterial.getCompose()
					.split(",");

		if (arrNeedId != null) {
			for (String strNeedId : arrNeedId) {
				if (StringUtils.isNullOrZero(strNeedId)) {
					continue;
				}
				mLlNeed.addView(createMaterialImg(strNeedId));
			}
		}
		if (arrComposeId != null) {
			for (String strComposeId : arrComposeId) {
				if (StringUtils.isNullOrZero(strComposeId)) {
					continue;
				}
				mLlCompose.addView(createMaterialImg(strComposeId));
			}
		}

	}

	/**
	 * 设置查找物品详情失败的提示
	 */
	private void setNullHint() {

		mLlRoot.removeAllViews();
		TextView tvHint = new TextView(this);
		tvHint.setText("查找物品详情失败");
		mLlRoot.addView(tvHint);
	}

	/**
	 * 根据物品ID构建对应的ImageView,所需图片从服务器请求,点击事件也已在这里设置。
	 * @param materialId
	 * @return
	 */
	private ImageView createMaterialImg(final String materialId) {

		ImageView img = new ImageView(this);

		LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
					DeviceUtil.dp2Px(this, 40), DeviceUtil.dp2Px(this, 40));
		params.leftMargin = DeviceUtil.dp2Px(this, 2);
		params.rightMargin = DeviceUtil.dp2Px(this, 2);

		img.setLayoutParams(params);

		mImgLoader.displayImage(
					URLUtil.getURL_ZBImg(Integer.parseInt(materialId), EnumDPI.DPI64x64), img);

		img.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {

				openMaterialDetail(materialId);
			}
		});

		return img;
	}

	/**
	 * 打开物品详情界面
	 * @param materialId
	 */
	private void openMaterialDetail(String materialId) {

		Intent it = new Intent(this, MaterialDetailActivity.class);
		it.putExtra(MaterialDetailActivity.EXTRA_MATERIALID, materialId);
		startActivity(it);
		overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
	}

}

/**
 * 物品bean
 * @author warren
 * @date 2014年12月30日
 */
public class Material {

	private String id;
	private String name;
	private String description;
	private Map<String, String> extAttrs;
	private String need;
	private String compose;
	private String extDesc;
	private int price;
	private int allPrice;
	private int sellPrice;
	private String tags;
	
	public Material() {
		super();
	}
	
	/**
	 * @return the id
	 */
	public String getId() {
		return id;
	}
	/**
	 * @param id the id to set
	 */
	public void setId(String id) {
		this.id = id;
	}
	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}
	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}
	/**
	 * @return the description
	 */
	public String getDescription() {
		return description;
	}
	/**
	 * @param description the description to set
	 */
	public void setDescription(String description) {
		this.description = description;
	}
	/**
	 * @return the extAttrs
	 */
	public Map<String, String> getExtAttrs() {
		return extAttrs;
	}
	/**
	 * @param extAttrs the extAttrs to set
	 */
	public void setExtAttrs(Map<String, String> extAttrs) {
		this.extAttrs = extAttrs;
	}
	/**
	 * @return the need
	 */
	public String getNeed() {
		return need;
	}
	/**
	 * @param need the need to set
	 */
	public void setNeed(String need) {
		this.need = need;
	}
	/**
	 * @return the compose
	 */
	public String getCompose() {
		return compose;
	}
	/**
	 * @param compose the compose to set
	 */
	public void setCompose(String compose) {
		this.compose = compose;
	}
	/**
	 * @return the extDesc
	 */
	public String getExtDesc() {
		return extDesc;
	}
	/**
	 * @param exDesc the exDesc to set
	 */
	public void setExtDesc(String extDesc) {
		this.extDesc = extDesc;
	}
	/**
	 * @return the price
	 */
	public int getPrice() {
		return price;
	}
	/**
	 * @param price the price to set
	 */
	public void setPrice(int price) {
		this.price = price;
	}
	/**
	 * @return the allPrice
	 */
	public int getAllPrice() {
		return allPrice;
	}
	/**
	 * @param allPrice the allPrice to set
	 */
	public void setAllPrice(int allPrice) {
		this.allPrice = allPrice;
	}
	/**
	 * @return the sellPrice
	 */
	public int getSellPrice() {
		return sellPrice;
	}
	/**
	 * @param sellPrice the sellPrice to set
	 */
	public void setSellPrice(int sellPrice) {
		this.sellPrice = sellPrice;
	}
	/**
	 * @return the tags
	 */
	public String getTags() {
		return tags;
	}
	/**
	 * @param tags the tags to set
	 */
	public void setTags(String tags) {
		this.tags = tags;
	}
	
	
}

/**
 * 简化版的物品bean
 * @author warren
 * @date 2014年12月31日
 */
public class MaterialSimple {
	
	private int id;
	private String text;

	public MaterialSimple(){
		
	}
	
	public MaterialSimple(int id, String text) {
		super();
		this.id = id;
		this.text = text;
	}

	/**
	 * @return the id
	 */
	public int getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(int id) {
		this.id = id;
	}

	/**
	 * @return the text
	 */
	public String getText() {
		return text;
	}

	/**
	 * @param text the text to set
	 */
	public void setText(String text) {
		this.text = text;
	}
}

四、实现效果



五、说明

1,这些代码中使用到的 jar 包有 universal-image-loader, android-async-http, jacksonjson.
2,图片的请求使用 universal-image-loader来做缓存就已足够,其他网络访问的请求 暂时只做了内存缓存,没有存放在本地;官方应用的很多数据都是存放在了本地的,本地数据过期时会提醒下载最新的数据包;本地缓存的功能留待以后做。
3,由于官方应用的很多数据做了本地缓存,所以在操作功能时经常会不用 请求服务器,为了方便分析,可以把本地缓存清掉。缓存路径为 /sdcard/lolboxcache/      。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值