Android开发实例-健康食谱应用(一)

本系列文章主要介绍如何利用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:提供食谱相关的处理逻辑功能

 软件主界面设计开发

 主界面主要提供了健康食谱的主分类控件,用户可以选择自己感兴趣的分类进行食谱查看。为了便于用户更好的找到自己需要的食谱,我们提供一个全局搜索功能,用户可以输入关键词搜索自己想要的食谱信息,如:萝卜,将加载出所有与萝卜相关的食谱

主界面最终效果图如下所示:
技术分享

主界面共分为两大块:全局搜索布局和Grid分类布局,其布局xml如下所示:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/list_item_bg" 
    tools:context=".MainActivity" >

    <RelativeLayout
        android:id="@+id/top_layout"
        android:layout_width="match_parent"
        android:layout_height="160dp"
        android:layout_alignParentTop="true"
        android:background="@drawable/main_top" >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="@drawable/main_title"
            android:text="@string/app_name"
            android:gravity="center"
            android:textColor="@android:color/white"
            android:textSize="18sp"
            android:layout_alignParentTop="true" >
        </TextView>

        <EditText
            android:id="@+id/main_input"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_alignParentBottom="true"
            android:layout_margin="20dp"
            android:background="@drawable/edit_shape"
            android:hint="@string/search_tip"
            android:lines="1"
            android:paddingLeft="32dp"
            android:paddingRight="32dp"
            android:singleLine="true"
            android:textSize="16sp" >
        </EditText>

        <ImageView
            android:id="@+id/main_clear_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/main_input"
            android:layout_alignLeft="@+id/main_input"
            android:layout_alignTop="@+id/main_input"
            android:src="@drawable/clear" />

        <ImageView
            android:id="@+id/main_search_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/main_input"
            android:layout_alignRight="@+id/main_input"
            android:layout_alignTop="@+id/main_input"
            android:src="@drawable/search" >
        </ImageView>
    </RelativeLayout>

    <GridView
        android:id="@+id/main_grid"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/top_layout"
        android:layout_marginTop="20dp"
        android:gravity="center_horizontal"
        android:numColumns="3"
        android:scrollbars="none"
        android:verticalSpacing="20dp" >
    </GridView>

</RelativeLayout>

通过 http://api.yi18.net/cook/cookclass接口可以获取到所有的主分类信息。我们通过定制自有的MainGridAdapter实现GridView的渲染,源码如下所示:
import java.util.List;

import android.content.Context;
import android.content.Intent;
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;

public class MainGridAdapter extends BaseAdapter {
	
	private List<Item> items;
	private Context ctx;
	
	public MainGridAdapter(List<Item> items, Context ctx) {
		this.items = items;
		this.ctx = ctx;
	}

	@Override
	public int getCount() {
		return items.size();
	}

	@Override
	public Object getItem(int arg0) {
		return items.get(arg0);
	}

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

	@Override
	public View getView(final int position, View view, ViewGroup arg2) {
		if(view == null) {
			view = LayoutInflater.from(ctx).inflate(R.layout.item, null);
		}
		final Item item = items.get(position);
		ImageView icon = (ImageView) view.findViewById(R.id.item_icon);
		icon.setImageResource(item.icon);
		TextView text = (TextView) view.findViewById(R.id.item_text);
		text.setText(ctx.getString(item.text));
		view.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				Intent intent = new Intent();
				intent.putExtra("id", position+1);
				intent.putExtra("title", ctx.getString(item.text));
				intent.setClass(ctx, MListActivity.class);
				ctx.startActivity(intent);
			}
		});
		return view;
	}
	
	static class Item {
		
		public Item() {
			
		}
		
		public Item(int bgColor, int icon, int text) {
			super();
			this.bgColor = bgColor;
			this.icon = icon;
			this.text = text;
		}
		
		public int bgColor;
		public int icon;
		public int text;
	}

}
其中Item为保存了背景图、分类图标和分类名称的模型对象。我们为了实现方便,将背景图忽略。MainGridAdapter为每个分类提供了单击事件,点击后将进入子分类列表:
view.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				Intent intent = new Intent();
				intent.putExtra("id", position+1);
				intent.putExtra("title", ctx.getString(item.text));
				intent.setClass(ctx, MListActivity.class);
				ctx.startActivity(intent);
			}
		});

该Adapter使用方式如下所示:
private void initGrid() {
    	List<Item> items = new ArrayList<Item>();
    	items.add(new Item(R.color.item1, R.drawable.item1, R.string.item1));
    	items.add(new Item(R.color.item2, R.drawable.item2, R.string.item2));
    	items.add(new Item(R.color.item3, R.drawable.item3, R.string.item3));
    	items.add(new Item(R.color.item4, R.drawable.item4, R.string.item4));
    	items.add(new Item(R.color.item5, R.drawable.item5, R.string.item5));
    	items.add(new Item(R.color.item6, R.drawable.item6, R.string.item6));
    	items.add(new Item(R.color.item7, R.drawable.item7, R.string.item7));
    	items.add(new Item(R.color.item8, R.drawable.item8, R.string.item8));
    	items.add(new Item(R.color.item9, R.drawable.item9, R.string.item9));
    	items.add(new Item(R.color.item9, R.drawable.item10, R.string.item10));
    	items.add(new Item(R.color.item9, R.drawable.item11, R.string.item11));
    	items.add(new Item(R.color.item9, R.drawable.item12, R.string.item12));
    	items.add(new Item(R.color.item9, R.drawable.item13, R.string.item13));
    	items.add(new Item(R.color.item9, R.drawable.item14, R.string.item14));
    	grid.setAdapter(new MainGridAdapter(items, this));
    }
下面给出主界面Activity:
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.Toast;


public class MainActivity extends Activity implements OnClickListener {
	
	private GridView grid;
	private ImageView clearButton, searchButton;
	private EditText input;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        clearButton = (ImageView) findViewById(R.id.main_clear_btn);
        clearButton.setOnClickListener(this);
        searchButton = (ImageView) findViewById(R.id.main_search_btn);
        searchButton.setOnClickListener(this);
        input = (EditText) findViewById(R.id.main_input);
        grid = (GridView) findViewById(R.id.main_grid);
        initGrid();
    }

    private void initGrid() {
    	List<Item> items = new ArrayList<Item>();
    	items.add(new Item(R.color.item1, R.drawable.item1, R.string.item1));
    	items.add(new Item(R.color.item2, R.drawable.item2, R.string.item2));
    	items.add(new Item(R.color.item3, R.drawable.item3, R.string.item3));
    	items.add(new Item(R.color.item4, R.drawable.item4, R.string.item4));
    	items.add(new Item(R.color.item5, R.drawable.item5, R.string.item5));
    	items.add(new Item(R.color.item6, R.drawable.item6, R.string.item6));
    	items.add(new Item(R.color.item7, R.drawable.item7, R.string.item7));
    	items.add(new Item(R.color.item8, R.drawable.item8, R.string.item8));
    	items.add(new Item(R.color.item9, R.drawable.item9, R.string.item9));
    	items.add(new Item(R.color.item9, R.drawable.item10, R.string.item10));
    	items.add(new Item(R.color.item9, R.drawable.item11, R.string.item11));
    	items.add(new Item(R.color.item9, R.drawable.item12, R.string.item12));
    	items.add(new Item(R.color.item9, R.drawable.item13, R.string.item13));
    	items.add(new Item(R.color.item9, R.drawable.item14, R.string.item14));
    	grid.setAdapter(new MainGridAdapter(items, this));
    }
    
    @Override
    public void onClick(View v) {
    	switch (v.getId()) {
		case R.id.main_clear_btn:
			input.setText("");
			break;
		case R.id.main_search_btn:
			String searchStr = input.getText().toString();
			if(searchStr == null || searchStr.trim().equals("")) {
				Toast.makeText(this, "请输入要搜索的菜谱!", Toast.LENGTH_SHORT).show();
			} else {
				Intent intent = new Intent();
				intent.putExtra("keyword", searchStr);
				intent.setClass(this, CListActivity.class);
				startActivity(intent);
			}
			break;

		default:
			break;
		}
    }
    
}

子分类界面设计开发

当用户点击主界面的主分类条目时,将进入该分类的子分类列表界面。子分类接口为:http://api.yi18.net/cook/cookclass?id=myid
我们在MUtils中提供获取子分类的方法:
@SuppressWarnings("serial")
	public static ArrayList<HashMap<String, Object>> getChildClass(final int pid) {
		String url = "http://api.yi18.net/cook/cookclass";
		String result = HttpUtils.httpGet(url, new HashMap<String, Object>(){{
			put("id", pid);
		}});
		ArrayList<HashMap<String, Object>> dataMap = new  ArrayList<HashMap<String, Object>>();
		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++) {
						HashMap<String, Object> data = new HashMap<String, Object>();
						JSONObject obj = datas.getJSONObject(i);
						data.put("id", obj.getInt("id"));
						data.put("name", obj.getString("name"));
						dataMap.add(data);
					}
				}
			} catch (Exception e) {
			}
		}
		return dataMap;
	}

返回ArrayList<HashMap>是为了便于SimpleAdapter直接使用,使用见如下的MListActivity中的相关代码:
import java.util.ArrayList;
import java.util.HashMap;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;

import com.my.lib.Utils;

public class MListActivity extends Activity implements OnClickListener, OnItemClickListener, Runnable {
	
	private ImageView unconnect;
	private ListView mlist;
	Handler handler;
	ArrayList<HashMap<String, Object>> mdata;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.list);
		findViewById(R.id.list_return).setOnClickListener(this);
		mlist = (ListView) findViewById(R.id.mlist);
		mlist.setOnItemClickListener(this);
		unconnect = (ImageView) findViewById(R.id.list_unconnect);
		unconnect.setOnClickListener(this);
		((TextView)findViewById(R.id.list_title)).setText(getIntent().getStringExtra("title"));
		handler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				if(mdata == null) {
					change(true);
				} else {
					change(false);
					mlist.setAdapter(new SimpleAdapter(MListActivity.this, mdata, R.layout.list_item, new String[]{"name"}, new int[]{R.id.list_item_text}));
				}
			}
		};
		new Thread(this).start();
	}
	
	@Override
	@SuppressWarnings("unchecked")
	public void onItemClick(AdapterView<?> av, View v, int position, long arg3) {
		HashMap<String, Object> item = (HashMap<String, Object>)av.getItemAtPosition(position);
		Intent intent = new Intent();
		intent.putExtra("id", (Integer)item.get("id"));
		intent.putExtra("title", (String)item.get("name"));
		intent.setClass(this, CListActivity.class);
		startActivity(intent);
	}
	
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.list_return:
			MListActivity.this.finish();
			break;
		case R.id.list_unconnect:
			new Thread(this).start();
			break;
		default:
			break;
		}
	}
	
	@Override
	public void run() {
		if(Utils.canAccessNetwork(MListActivity.this)) {
			mdata = MUtils.getChildClass(getIntent().getIntExtra("id", 1));
		} 
		handler.sendEmptyMessage(1);
	}
	
	void change(boolean flag) {
		if(flag) {
			mlist.setVisibility(View.GONE);
			unconnect.setVisibility(View.VISIBLE);
		} else {
			mlist.setVisibility(View.VISIBLE);
			unconnect.setVisibility(View.GONE);
		}
	}
}
需要注意的是:Http请求处理不能在主线程中调用,需要开启新的线程调用。子分类点击后将进入该子分类的食谱列表

分类列表布局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/list_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/list_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/mlist"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none" >
    </ListView>

    <ImageView
        android:id="@+id/list_unconnect"
        android:layout_marginTop="160dp"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/unconnect" 
        android:visibility="gone"/>

</LinearLayout>

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="50dp"
    android:background="@drawable/list_selector" >
    
    <ImageView
        android:id="@+id/mlist_item_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:src="@drawable/list_item_icon"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp" />

    <TextView
        android:id="@+id/list_item_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@+id/mlist_item_icon"
        android:textColor="@android:color/white"
        android:textSize="16sp" />

    <ImageView
        android:id="@+id/mlist_item_arrow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/arrowr"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="20dp" />

</RelativeLayout>






郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。