当前位置: 代码迷 >> Android >> Android开发实例-健康菜谱应用(二)
  详细解决方案

Android开发实例-健康菜谱应用(二)

热度:97   发布时间:2016-04-28 01:44:48.0
Android开发实例-健康食谱应用(二)

转载请注明出处:http://blog.csdn.net/einarzhang/article/details/44806975

本系列文章主要介绍如何利用Android开发一个简单的健康食谱软件。用到的相关技术如下所示:

  • 提供GridView和ListView的基本使用
  • 利用universal-image-loader异步加载网络图片
  • 通过HttpClient获取网络http请求数据
  • 滑动分页加载数据
软件所用的所有数据均来源于http://doc.yi18.net/cookwendang提供的食谱接口,感谢他们!

软件文件结构如下所示:

MainActivity:主界面Acitivity
MListActivity:子分类列表Acitivity
CListActivity:食谱列表Activity
DetailActivity:食谱详情Activity

MainGridAdapter:主界面食谱分类适配器
CListAdapter:食谱列表适配器
Cook:用于保存食谱信息的pojo

HttpUtils:提供Http请求相关功能
MUtils:提供食谱相关的处理逻辑功能



搜索食谱

我们在主界面提供了搜索食谱功能,当用户输入食谱名称时,软件将给用户展现出想要的食谱列表。搜索食谱我们主要用到http://api.yi18.net/cook/search接口。
我们在MUtils方法增加搜索食谱方法:
public static ArrayList<Cook> search(final String keyword, final int page) {		String url = "http://api.yi18.net/cook/search";		String result = HttpUtils.httpGet(url, new HashMap<String, Object>(){{			put("keyword", URLEncoder.encode(keyword));			put("page", page);			put("limit", 20);		}});		ArrayList<Cook> dataMap = new  ArrayList<Cook>();		if(result != null) {			try {				JSONObject root = new JSONObject(result);				if(root.getBoolean("success")) {					JSONArray datas = root.getJSONArray("yi18");					for(int i = 0, len = datas.length(); i < len; i++) {						JSONObject obj = datas.getJSONObject(i);						Cook c = new Cook();						c.id = obj.optInt("id");						c.name = obj.optString("name");						if(c.name != null) {							c.name = c.name.replace("<font color=\"red\">", "").replace("</font>", "");						}						c.food = obj.optString("description");						c.img = obj.optString("img");						c.tag = obj.optString("keywords");						dataMap.add(c);					}				}			} catch (Exception e) {			}		}		return dataMap;	}
该搜索方法提供了食谱名称和分页页码参数,便于前端实现分页刷新功能,因为返回的食谱名称中包含Html信息,我们将其过滤掉。搜索结果将进入食谱列表界面,界面展示出搜索的结果信息

获取分类食谱

当用户点击某一子分类时,将获取出分类下的食谱列表,主要用到http://api.yi18.net/cook/list接口
我们在MUtils中增加如下方法:
public static ArrayList<Cook> getCooks(final int classId, final int page, final int sortType) {		String url = "http://api.yi18.net/cook/list";		String result = HttpUtils.httpGet(url, new HashMap<String, Object>(){{			put("id", classId);			put("page", page);			put("limit", 20);			put("type", sortType == SORT2 ? "count" : "id");		}});		ArrayList<Cook> dataMap = new  ArrayList<Cook>();		if(result != null) {			try {				JSONObject root = new JSONObject(result);				if(root.getBoolean("success")) {					JSONArray datas = root.getJSONArray("yi18");					for(int i = 0, len = datas.length(); i < len; i++) {						JSONObject obj = datas.getJSONObject(i);						Cook c = new Cook();						c.id = obj.optInt("id");						c.name = obj.optString("name");						c.count = obj.optInt("count");						c.fcount = obj.optInt("fcount");						c.rcount = obj.optInt("rcount");						c.food = obj.optString("food");						c.img = obj.optString("img");						c.tag = obj.optString("tag");						dataMap.add(c);					}				}			} catch (Exception e) {			}		}		return dataMap;	}
同搜索食谱功能一致,这里提供page对象,便于提供分页加载


食谱列表界面

食谱列表界面用于展示匹配相应结果的食谱列表,为了用户更好的了解食谱,在列表中我们提供缩略图展示。当用户搜索食谱或者点击某一分类时,将进入食谱列表界面。
食谱列表界面最终效果图如下所示:



每个列表展示出食谱的缩略图、食谱名称和食谱涉及的材料。列表布局XML设计如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@drawable/list_item_bg"     android:orientation="vertical" >    <RelativeLayout        android:layout_width="match_parent"        android:layout_height="50dp"        android:background="@drawable/title_bg" >        <ImageView            android:id="@+id/clist_return"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_alignParentLeft="true"            android:layout_centerVertical="true"            android:layout_marginLeft="20dp"            android:src="@drawable/arrowl" />        <TextView            android:id="@+id/clist_title"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:textColor="@android:color/white"            android:textSize="18sp"            android:textStyle="bold" />    </RelativeLayout>    <ListView        android:id="@+id/clist"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:scrollbars="none" >    </ListView></LinearLayout>

每个Item的布局clist_item.xml如下所示:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="150dp"    android:background="@drawable/list_selector" >    <ImageView        android:id="@+id/clist_item_icon"        android:layout_width="100dp"        android:layout_height="60dp"        android:layout_margin="10dp"        android:scaleType="fitCenter"        android:layout_alignParentLeft="true"        android:layout_centerVertical="true"         />    <LinearLayout        android:layout_centerVertical="true"        android:layout_toRightOf="@+id/clist_item_icon"        android:layout_toLeftOf="@+id/clist_item_arrow"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:orientation="vertical" >        <TextView            android:id="@+id/clist_item_title"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:textColor="@android:color/white"            android:textSize="18sp" />        <TextView            android:id="@+id/clist_item_detail"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:singleLine="true"            android:lines="1"            android:ellipsize="end"            android:textColor="@color/clist_item_tag"            android:textSize="14sp" />    </LinearLayout>    <ImageView        android:id="@+id/clist_item_arrow"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentRight="true"        android:layout_centerVertical="true"        android:layout_marginRight="20dp"        android:layout_marginLeft="10dp"        android:src="@drawable/arrowr" /></RelativeLayout>

我们利用clist_item.xml渲染自定义的ListAdapter,取名CListApdater,其代码如下所示:
import java.util.List;import android.content.Intent;import android.graphics.Bitmap;import android.view.LayoutInflater;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;import com.nostra13.universalimageloader.core.DisplayImageOptions;import com.nostra13.universalimageloader.core.ImageLoader;import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;import com.nostra13.universalimageloader.core.assist.ImageScaleType;public class CListAdapter extends BaseAdapter {		private CListActivity activity;		private List<Cook> dataList;		public CListAdapter(CListActivity activity, List<Cook> dataList) {		this.activity = activity;		this.dataList = dataList;		//初始化Android-Universal-Image-Loader框架		ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(activity));	}	@Override	public int getCount() {		return dataList.size();	}	@Override	public Object getItem(int position) {		return position;	}	@Override	public long getItemId(int position) {		return position;	}	@Override	public View getView(final int position, View convertView, ViewGroup parent) {		convertView = LayoutInflater.from(activity).inflate(R.layout.clist_item, null);		final Cook c = dataList.get(position);		((TextView)convertView.findViewById(R.id.clist_item_title)).setText(c.name);		((TextView)convertView.findViewById(R.id.clist_item_detail)).setText(c.tag);		//设置图片显示格式(我们可以设置圆角、缓存等一些列配置)		DisplayImageOptions options = new DisplayImageOptions.Builder()  	        .showImageOnLoading(R.drawable.loading)  	//加载中显示的正在加载中的图片	        .showImageOnFail(R.drawable.loading)  		//为了方便,加载失败也显示加载中的图片	        .cacheInMemory(true)  					//在内存中缓存图片	        .cacheOnDisk(true)  						        .bitmapConfig(Bitmap.Config.RGB_565)	        .imageScaleType(ImageScaleType.EXACTLY)		//设置图片以如何的编码方式显示	        .build(); 		//异步加载图片,并渲染到指定的控件上		ImageLoader.getInstance().displayImage(MUtils.PREFIX_IMG + c.img, (ImageView)convertView.findViewById(R.id.clist_item_icon), options);		//Item点击后进入食谱详情界面		convertView.setOnClickListener(new OnClickListener() {			@Override			public void onClick(View v) {				Intent intent = new Intent(activity, DetailActivity.class);				intent.putExtra("id", c.id);				intent.putExtra("title", c.name);				activity.startActivity(intent);			}		});		return convertView;	}	//为Adapter增加新的食谱数据,当分页加载时,用于更新分页信息	public void add(List<Cook> newData) {		dataList.addAll(newData);	}}

CListAcitivity同时实现了搜索和分类进入的展示功能,并加入下拉分页加载,其代码如下所示:
import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Handler.Callback;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.ListView;import android.widget.TextView;public class CListActivity extends Activity implements OnClickListener, OnItemClickListener, Runnable {		int lastVisibleIndex = 0;	//滚动的最后可见条目	int page = 1;	//当前分页页码	int cId;		//分类ID	String keyword;	//搜索关键词	CListAdapter mAdapter;		List<Cook> cList;	ListView mlistView;		@Override	protected void onCreate(Bundle savedInstanceState) {		super.onCreate(savedInstanceState);		setContentView(R.layout.clist);		findViewById(R.id.clist_return).setOnClickListener(this);		mlistView = (ListView) findViewById(R.id.clist);		mlistView.setOnItemClickListener(this);		mlistView.setOnScrollListener(scrollListener);		cId = getIntent().getIntExtra("id", 0);		if(cId == 0) {			//食谱搜索			keyword = getIntent().getStringExtra("keyword");			((TextView)findViewById(R.id.clist_title)).setText(keyword);		} else {			//分类进入			((TextView)findViewById(R.id.clist_title)).setText(getIntent().getStringExtra("title"));		}		cList = new ArrayList<Cook>();		new Thread(this).start();	}		@Override	public void onItemClick(AdapterView<?> av, View arg1, int arg2, long arg3) {			}		@Override	public void onClick(View v) {		switch (v.getId()) {		case R.id.clist_return:			CListActivity.this.finish();			break;		default:			break;		}	}		@Override	public void run() {		loadData();		handler.sendEmptyMessage(0);	}		public void loadData() {		List<Cook> results;		if(cId == 0) {			results = MUtils.search(keyword, page++);		} else {			results = MUtils.getCooks(cId, page++, MUtils.SORT1);		}		//获取成功,则更新数据列表		cList.addAll(results);	}		Handler handler = new Handler(new Callback() {		@Override		public boolean handleMessage(Message msg) {			//更新列表界面			if(mAdapter == null) {				mAdapter = new CListAdapter(CListActivity.this, cList);				mlistView.setAdapter(mAdapter);			} else {				mAdapter.notifyDataSetChanged();			}			return false;		}	});		OnScrollListener scrollListener = new OnScrollListener() {		@Override		public void onScrollStateChanged(AbsListView view, int scrollState) {			int itemsLastIndex = mAdapter.getCount() - 1; // 数据集最后一项的索引			if ((scrollState == SCROLL_STATE_TOUCH_SCROLL || scrollState == SCROLL_STATE_IDLE)					&& lastVisibleIndex == itemsLastIndex) {				//当滚动最后一项时,加载新的一页数据				new Thread(CListActivity.this).start();			}		}		@Override		public void onScroll(AbsListView view, int firstVisibleItem,				int visibleItemCount, int totalItemCount) {			//滚动过程中更新最后可见索引			lastVisibleIndex = firstVisibleItem + visibleItemCount - 1;		}	};}

从代码中可以看到,我们通过为ListView定制OnScrollListener来实现滚动的分页加载逻辑

食谱详情

食谱详情用户展示食谱的详细信息,用户可以在详情里面看到食物的具体制作方法。
最终效果图如下所示:



我们首先在MUtils加上获取详情的功能:
public static Cook show(final int id) {		String url = "http://api.yi18.net/cook/show";		String result = HttpUtils.httpGet(url, new HashMap<String, Object>(){{			put("id", id);		}});		if(result != null) {			try {				JSONObject root = new JSONObject(result);				if(root.getBoolean("success")) {					JSONObject obj = root.getJSONObject("yi18");					Cook c = new Cook();					c.id = obj.optInt("id");					c.name = obj.optString("name");					c.count = obj.optInt("count");					c.fcount = obj.optInt("fcount");					c.rcount = obj.optInt("rcount");					c.food = obj.optString("food");					c.img = obj.optString("img");					c.tag = obj.optString("tag");					c.message = obj.optString("message");					return c;				}			} catch (Exception e) {			}		}		return null;	}	

详情的布局xml如下所示:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@drawable/list_item_bg" >    <RelativeLayout        android:id="@+id/detail_layout"        android:layout_width="match_parent"        android:layout_height="50dp"        android:layout_alignParentTop="true"        android:background="@drawable/title_bg" >        <ImageView            android:id="@+id/detail_return"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_alignParentLeft="true"            android:layout_centerVertical="true"            android:layout_marginLeft="20dp"            android:src="@drawable/arrowl" />        <TextView            android:id="@+id/detail_title"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:textColor="@android:color/white"            android:textSize="18sp"            android:textStyle="bold" />    </RelativeLayout>    <ScrollView        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_below="@+id/detail_layout"        android:scrollbars="none" >        <LinearLayout            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:gravity="center_horizontal"            android:orientation="vertical" >            <ImageView                android:id="@+id/detail_img"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_margin="10dp" >            </ImageView>            <TextView                android:id="@+id/detail_name"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_marginBottom="10dp"                android:textColor="@color/clist_item_tag"                android:textSize="20sp" />            <TextView                android:id="@+id/detail_food"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_marginBottom="10dp"                android:textSize="14sp" />            <ImageView                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:layout_marginBottom="10dp"                android:background="@drawable/lines" />            <TextView                android:id="@+id/detail_text"                android:layout_width="match_parent"                android:layout_height="match_parent"                android:layout_marginBottom="10dp"                android:padding="20dp"                android:textSize="16sp" />        </LinearLayout>    </ScrollView></RelativeLayout>

DetailActivity的完整实现代码如下所示:
import com.nostra13.universalimageloader.core.ImageLoader;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.text.Html;import android.view.View;import android.view.View.OnClickListener;import android.widget.ImageView;import android.widget.TextView;public class DetailActivity extends Activity implements OnClickListener, Runnable {	private TextView titleView, contentView, foodView, nameView;	private ImageView imgView;	private int id;	private Cook cook;		@Override	protected void onCreate(Bundle savedInstanceState) {		super.onCreate(savedInstanceState);		setContentView(R.layout.detail);		titleView = (TextView) findViewById(R.id.detail_title);		contentView = (TextView) findViewById(R.id.detail_text);		foodView = (TextView) findViewById(R.id.detail_food);		nameView = (TextView) findViewById(R.id.detail_name);		imgView = (ImageView) findViewById(R.id.detail_img);		findViewById(R.id.detail_return).setOnClickListener(this);		initData();	}		private void initData() {		titleView.setText(getIntent().getStringExtra("title"));		id = getIntent().getIntExtra("id", 1);		new Thread(this).start();	}		@Override	public void run() {		cook = MUtils.show(id);		handler.sendEmptyMessage(0);	}		@Override	public void onClick(View v) {		switch (v.getId()) {		case R.id.detail_return:			DetailActivity.this.finish();			break;		default:			break;		}	}		Handler handler = new Handler() {		@Override		public void handleMessage(Message msg) {			//异步显示图片			ImageLoader.getInstance().displayImage(MUtils.PREFIX_IMG + cook.img, imgView);			nameView.setText(cook.name);			foodView.setText(cook.food);			//内容以Html的方式展示			contentView.setText(Html.fromHtml(cook.message));		}	};}

至此,完整的健康食谱软件开发完成

  相关解决方案