• 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));
    		}
    	};
    }
    

    至此,完整的健康食谱软件开发完毕
    完整的代码详见git链接:https://github.com/einarzhang/CookBook
  • 相关阅读:
    iOS真机测试中出现dyld`dyld_fatal_error错误
    给WKWebView添加进度条(swift)
    手机号、密码正则判断
    Xcode8 上架前属性列表添加权限
    系统定位
    修改UISearchBar的背景颜色
    iOS 给NSString文字上添加横线 中间和下划线
    iOS UISearchBar 设置取消按钮,回收键盘,并修改cancel为“取消”
    iOS 支付宝第三方使用步骤
    UIImagePickerController和UIAlertController结合使用
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/6900576.html
Copyright © 2020-2023  润新知