标签:android
整体是通过2个Fragment+FragmentTabHost组合而成,本节主要针对Fragment1做一个总结,该Fragment主要实现以下功能
【1】输入快递单号或者扫描二维码 查询快递信息
【2】侧滑栏集成的百度地图可以显示附近快递点,方便选择合适自己的地点
本篇只总结主界面架构和功能1,先上图有个直观感受
Fragment+FragmentTabHost组合而成,其中整体的布局文件是在LinearLayout(vertical)的布局中嵌入了FrameLayout,上图显示的文字和背景都在直接代码中加入的,main_tab_layout.xml具体布局如下
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:baiduadsdk="http://schemas.android.com/apk/res/com.weimeijing.feigeshudi" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <FrameLayout android:id="@+id/realtabcontent" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" /> <android.support.v4.app.FragmentTabHost android:id="@android:id/tabhost" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/maintab_toolbar_bg" > <FrameLayout android:id="@android:id/tabcontent" android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="0" /> </android.support.v4.app.FragmentTabHost> </LinearLayout>
框架MainTabActivity主要使用FragmentTabHost来管理,这里将俩个Fragment放入一个数组中
// 定义数组来存放Fragment界面 private Class fragmentArray[] = { MainActivity.class, MyExpressActivity.class };然后// 将Tab按钮添加进Tab选项卡中 mTabHost.addTab(tabSpec, fragmentArray[i], null);
具体代码如下
package com; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentTabHost; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.TabHost.TabSpec; import android.widget.TextView; import com.baidu.mapapi.map.BaiduMap; import com.weimeijing.feigeshudi.R; /** * @author xsfelvis 功能描述:自定义TabHost */ public class MainTabActivity extends FragmentActivity { // 定义FragmentTabHost对象 private FragmentTabHost mTabHost; // 定义一个布局 private LayoutInflater layoutInflater; // 定义数组来存放Fragment界面 private Class fragmentArray[] = { MainActivity.class, MyExpressActivity.class }; // 定义数组来存放按钮图片 private int mImageViewArray[] = { R.drawable.tab_square_btn, R.drawable.tab_home_btn }; // Tab选项卡的文字 private String mTextviewArray[] = { "快递追踪", "我的收藏" }; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_tab_layout); initView(); } /** * 初始化组件 */ private void initView() { // 实例化布局对象 layoutInflater = LayoutInflater.from(this); // 实例化TabHost对象,得到TabHost mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost); mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent); // 得到fragment的个数 int count = fragmentArray.length; for (int i = 0; i < count; i++) { // 为每一个Tab按钮设置图标、文字和内容 TabSpec tabSpec = mTabHost.newTabSpec(mTextviewArray[i]) .setIndicator(getTabItemView(i)); // 将Tab按钮添加进Tab选项卡中 mTabHost.addTab(tabSpec, fragmentArray[i], null); // 设置Tab按钮的背景 mTabHost.getTabWidget().getChildAt(i) .setBackgroundResource(R.drawable.selector_tab_background); } } /** * 给Tab按钮设置图标和文字 */ private View getTabItemView(int index) { View view = layoutInflater.inflate(R.layout.tab_item_view, null); ImageView imageView = (ImageView) view.findViewById(R.id.imageview); imageView.setImageResource(mImageViewArray[index]); TextView textView = (TextView) view.findViewById(R.id.textview); textView.setText(mTextviewArray[index]); return view; } }
这里的布局文件非常重要,因为Fragment1里面实际包含了2部分 快递追踪+侧滑(来自github的开源控件开源控件)下一篇讲侧滑模块再详细介绍,布局文件中activity_main.xml其中 <include layout="@layout/left_slide" />是侧滑的布局文件,这里我们暂不考虑,
【1】使用了SlidingMenu,整个布局要在其中包含跟一般的布局文件不一样,这里需要导入slidingmenu_library才可以使用
【2】要注意由于要包含侧滑部分,因此整个LinearLayout属性使用"horizontal",而不是Vertical的
布局文件代码如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:baiduadsdk="http://schemas.android.com/apk/res/com.weimeijing.feigeshudi" xmlns:xsf="http://schemas.android.com/apk/res/com.weimeijing.feigeshudi" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" > <span style="color:#009900;"> <com.leftside.SlidingMenu android:id="@+id/id_menu" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/img_frame_background" xsf:rightPadding="80dp" ></span> <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal" > <span style="color:#006600;"> <include layout="@layout/left_slide" /></span> <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/main_banner2" android:orientation="vertical" tools:context=".MainActivity" > <LinearLayout android:id="@+id/ll_main_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/iv_main_banner" android:focusable="true" android:focusableInTouchMode="true" android:orientation="vertical" android:paddingLeft="15dp" android:paddingRight="15dp" android:paddingTop="60dp" > <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <EditText android:id="@+id/tv_main_express_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:background="@drawable/edittext_selector" android:focusable="false" android:hint="快递公司" android:textSize="25dp" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:gravity="center|center_vertical" android:src="@drawable/image_search_arrow" /> </RelativeLayout> <TextView android:id="@+id/tv_main_banner_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="right|center_vertical" android:paddingRight="10dp" android:text="@string/banner_text" android:textColor="#FFFAFAFA" android:textSize="8pt" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <EditText android:id="@+id/tv_main_express_number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentRight="true" android:background="@drawable/edittext_selector" android:clickable="true" android:hint="快递单号" android:inputType="number" android:textSize="25dp" /> <ImageView android:id="@+id/scane_express_number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="10dp" android:layout_marginTop="2dp" android:clickable="true" android:src="@drawable/button_selector_scane" /> </RelativeLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <Button android:id="@+id/btn_main_search" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_weight="3" android:background="@drawable/button_selector_search" android:text="查询" android:textColor="#FFfcfcfc" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" /> <Button android:id="@+id/btn_main_reset" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_weight="3" android:background="@drawable/button_selector_reset" android:text="重置" android:textColor="#FFfcfcfc" /> </LinearLayout> </LinearLayout> </LinearLayout> </LinearLayout> <span style="color:#006600;"> </com.leftside.SlidingMenu></span> </RelativeLayout>
这里对该代码中一些要点做下总结:
【1】使用fragment与Activity通信
因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为:
a、如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法
b、如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。
c、在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。
【2】使用Fragment时 onCreateView()参数详解
/* 实现fragment需要使用Fragment时,需要继承Fragment或者Fragment的子类 */ // onCreateView()中container参数代表该Fragment在Activity中的父控件;savedInstanceState提供了上一个实例的数据 // 为Fragment加载布局时调用 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // 为这个 fragment加载 activity_main布局文件 /* * 第一个是resource ID,指明了当前的Fragment对应的资源文件;第二个参数是父容器控件; * 第三个布尔值参数表明是否连接该布局和其父容器控件 * ,在这里的情况设置为false,因为系统已经插入了这个布局到父控件,设置为true将会产生多余的一个View Group。 */ return inflater.inflate(R.layout.activity_main, null); }
1、startActivity( )
仅仅是跳转到目标页面,若是想跳回当前页面,则必须再使用一次startActivity( )。
2、startActivityForResult( )
可 以一次性完成这项任务,当程序执行到这段代码的时候,假若从T1Activity跳转到下一个Text2Activity,而当这个 Text2Activity调用了finish()方法以后(必须调用该方法!),程序会自动跳转回T1Activity,并调用前一个T1Activity中的 onActivityResult( )方法。通过requestCode来判断执行
相关函数:
startActivityForResult(Intent intent, Int requestCode)
setResut(int resultCode, Intent intent)
onActivityResult(int requestCode, int resultCode, Intent intent)
本项中通过俩种形式手动输入快递号requestCode=1,扫描二维码requestCode=0
startActivityForResult(openCameraIntent, 0); // 扫描二维码触发
startActivityForResult(intent, 1);//手动输入快递单号
/** * onActivityResult接收返回的数据/结果的处理函数 * 这里的requestCode就是startActivityForResult的requestCode, * resultCode就是setResult里面的resultCode, 返回的数据在data里面。 */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (data == null) { return; } super.onActivityResult(requestCode, resultCode, data); if (requestCode == 1) { // 说明是快递选择触发的 /* * 处理ExpressList回传来的点击选择的对应的code/name 将回传信息填写到对应的文本控件中 */ code = data.getStringExtra("code"); String name = data.getStringExtra("name"); tv_main_express_name.setText(name); tv_main_express_number.setText(""); } else if (requestCode == 0) { // 扫描按钮触发的 Bundle bundle = data.getExtras(); String scanResult = bundle.getString("result"); tv_main_express_number.setText(scanResult); } }
【4】这里在最开始就让MainActivity继承了 Fragment类 实现了 OnClickListener,以便通过switch统一处理监听对象,这种方式处理起来很简洁
/* 将监听放在一起统一处理 */ public void onClick(View v) { switch (v.getId()) { // 获取视图的ID进行判断从而做出相应的跳转,获取name参数 case R.id.scane_express_number: Intent openCameraIntent = new Intent(getActivity(), CaptureActivity.class); startActivityForResult(openCameraIntent, 0); // 0跟下面的onActivityResult方法对应,表明触发类型 case R.id.btn_main_reset: // 重置按钮,将快递单号重置为空 tv_main_express_number.setText(""); break; case R.id.tv_main_express_name: // 跳转到快递公司选择界面Activity,获取name参数 /* * ExpressList中通过getResources()获取string_city_code.xml文件中的定义好的快递公司名称数组 * 监听listview的选项item,获取快递公司名称name和对应拼音code封装在intent中回传过来 * 在onActivityResult根据其requestcode处理提取 */ Intent intent = new Intent(getActivity(), ExpressList.class); startActivityForResult(intent, 1); break; case R.id.btn_main_search: // 装化成为字符串进行比较判断 String name = tv_main_express_name.getText().toString(); if (name.equals("")) { Toast.makeText(getActivity(), "请选择快递公司", Toast.LENGTH_SHORT) .show(); } else { String number = tv_main_express_number.getText().toString(); if (number.equals("")) { Toast.makeText(getActivity(), "快递号码不能为空", Toast.LENGTH_SHORT).show(); } else { // searchForJson(number,name); // 在对应activity中创建ProgressDialog progressDialog = new ProgressDialog(getActivity()); // 核心所在!!! /* * 输入参数含义分别为: number:快递单号 name:快递名称 code:快递拼音名称 * getActivity()当前Activity/fragment progressDialog控件的名称 */ /* * 使用Xutil框架中的HttpUtils * 解析爱查快递提供信息使用Parcelable序列化方式将data和context保存到listInfo * ,使用intent跳转到ExpressInfoActivity */ <span style="color:#33CC00;"> <span style="color:#006600;">QueryExpressUtil.queryExpressForNumber(number, name, code, getActivity(), progressDialog);</span></span> } } break; default: break; } }
【5】关键处理函数QueryExpressUtil.queryExpressForNumber
QueryExpressUtil.queryExpressForNumber(number, name, code,getActivity(), progressDialog);
* 输入参数含义分别为: number:快递单号 name:快递名称 code:快递拼音名称
* getActivity()当前Activity/fragment progressDialog控件的名称
整体核心函数主要做了以下几件事情:
(1)使用Xutil框架中的HttpUtils通过Get的请求方式,GET请求的数据会附在URL之后(就是 把数据放置在HTTP协议头中),以?分割URL和传输数据,参数之间以&相连(对比下post)
String url; url = "http://api.ickd.cn/?id=102616&secret=16135ea51cb60246eff620f130a005bd&com="; url += code; url += "&nu="; url += number; url += "&type=json&encode=utf8&ord=asc"; Log.v("tag", url);// 测试log // HttpUtils使用方法:使用普通get方法 HttpUtils http = new HttpUtils(); http.send(HttpRequest.HttpMethod.GET, url, new RequestCallBack<String>()
(2) 解析爱查快递提供信息使用Parcelable序列化方式将data和context保存到listInfo其中ExpressInfo为序列化数据模型
/*将快递信息序列化*/ public class ExpressInfo implements Parcelable { //声明实现接口Parcelable public String time; public String context; //实例化静态内部对象CREATOR实现接口Parcelable.Creator public static final Parcelable.Creator<ExpressInfo> CREATOR = new Creator<ExpressInfo>() { @Override //从Parcel容器中读取传递数据值,封装成Parcelable对象返回逻辑层 public ExpressInfo createFromParcel(Parcel source) { //先读取time,再读取context ExpressInfo expressInfo = new ExpressInfo(); expressInfo.time = source.readString(); expressInfo.context = source.readString(); return expressInfo; } @Override //创建一个类型为T,长度为size的数组,仅一句话(return new T[size])即可。方法是供外部类反序列化本类数组使用。 public ExpressInfo[] newArray(int size) { return new ExpressInfo[size]; } }; //内容接口描述,默认返回0就可以; @Override public int describeContents() { return 0; } //该方法将类的数据写入外部提供的Parcel中.即打包需要传递的数据到Parcel容器保存,以便从parcel容器获取数据 @Override public void writeToParcel(Parcel dest, int flags) { //先写入time,再写入context dest.writeString(time); dest.writeString(context); } }
这里从服务器返回快递信息的是Json格式的数据,过程在Xutil框架中HttpUtils模块onSuccess函数中处理
@Override public void onSuccess(ResponseInfo<String> responseInfo) { try { JSONObject all = new JSONObject(responseInfo.result);// 获取所有的json对象 String status = all.getString("status");// 快递状态信息 // 0失败,1正常,2派送中,3已签收,4退回 if (status.equals(STATE_FAIL)) { // 0代表查找失败 String message = all.getString("message"); // message中存储着错误消息 Toast.makeText(context, message, Toast.LENGTH_LONG).show(); // 关闭ProgressDialog progressDialog.dismiss(); return; } // 通过inflate加载aty_express_info文件的控件 // jsonArray按照爱查快递解析出来data(time/context)其中context就是最重要的快递中转信息 JSONArray jsonArray = all.getJSONArray("data"); int length = jsonArray.length(); Log.v("tag", "长度是" + length); <span style="color:#006600;">// !!!!!!!定义调转意图为快递详细信息Activity Intent intent = new Intent(context,ExpressInfoActivity.class);</span> // 将快递信息封装到序列化的对象信息中 ArrayList<ExpressInfo> infoList = new ArrayList<ExpressInfo>(); /* * 扫描数据信息,按照json格式读取快递时间和快递信息,填入到infoList中, * ExpressInfo采用android常用的Parcelable来进行序列化 * 将序列化的数据存放到infolist中 */ for (int i = 0; i < length; i++) { ExpressInfo expressInfo = new ExpressInfo(); String time = jsonArray.getJSONObject(i).getString("time"); String context = jsonArray.getJSONObject(i).getString("context"); expressInfo.time = time; expressInfo.context = context; infoList.add(expressInfo); } // 利用Bundle回传数据 Bundle bundle = new Bundle(); bundle.putParcelableArrayList("infos", infoList);// intent中传递序列化对象 intent.putExtras(bundle);// 将序列化的bundle信息放入intent中进行传递 intent.putExtra("state", status);// 快递状态 intent.putExtra("name", name);// 快递名 intent.putExtra("number", number);// 快递编号 intent.putExtra("code", code);// 快递拼音 // 关闭ProgressDialog progressDialog.dismiss(); <span style="color:#009900;">// 将回传数据 context.startActivity(intent);// 跳转到ExpressInfoActivity.class</span> } catch (JSONException e) { e.printStackTrace(); } }
该Activity用来提取序列化的消息并将其显示(ListView)
/* QueryExpressUtil中带有参数intent跳转 */ // 提取序列化info对象 infoList = getIntent().getExtras().getParcelableArrayList("infos"); // 存储快递实时信息listview lv_express_info = (ListView) findViewById(R.id.lv_express_info_list); // 设置适配器,这里的listView对 lv_express_info.setAdapter(<span style="color:#009900;">new ExpressInfoAdapter(this, infoList)</span>); // // 获取上一个跳转Activity的Intent参数,快递单号,拼音,快递名称 number = getIntent().getStringExtra("number"); code = getIntent().getStringExtra("code"); name = getIntent().getStringExtra("name"); expressState = getIntent().getStringExtra("state"); // 根据状态设置信息显示侧栏的动作 if (expressState.equals(STATE_RECEIVED)) { // 快递已经签收 /* * ((TextView) findViewById(R.id.follow_textview)) * .setCompoundDrawablesWithIntrinsicBounds(0, 0, * R.drawable.icon_success, 0); */ findViewById(R.id.colorLine).setBackgroundResource( R.drawable.red_line_green); findViewById(R.id.dot_full).setVisibility(View.VISIBLE); } if (expressState.equals(STATE_ON_PASSAGE)) { // 快递正在派送中 findViewById(R.id.colorLine).setBackgroundResource( R.drawable.red_line_blue); }
在 ExpressInfoAdapter中,主要使用了ViewHolder/convertView来优化ListView
ViewHolder不是Android的开发API,而是一种设计方法,就是设计个静态类,缓存一下,省得Listview更新的时候,还要重新操作;
convertView 在API中的解释是The old view to reuse, if possible, 第一次getView时还没有convertView,这时你便创建了一个新的view,下次getView时就有这个“旧的”convertView 了 setTag的作用才是把查找的view通过ViewHolder封装好缓存起来方便多次重用,当需要时可以getTag拿出来
;
@Override public View getView(int position, View convertView, ViewGroup parent) { //优化listview的操作使用ViewHolder,将缓存的那些view封装好 ViewHolder holder; //第一次创建时缓存起来 if (convertView == null) { //得到LayoutInflater实例之后就可以用它来加载方法 convertView = mInflater.inflate(R.layout.list_express_info_item,parent, false); //静态加载的viewHolder holder = new ViewHolder(); holder.time = (TextView) convertView.findViewById(R.id.tv_info_time); holder.context = (TextView) convertView.findViewById(R.id.tv_info_context); //setTag的作用才是把查找的view通过ViewHolder封装好缓存起来方便多次重用,当需要时可以getTag拿出来 convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.time.setText(lists.get(position).time); holder.context.setText(lists.get(position).context); return convertView; }
【1】使用fragment与Activity通信(见上)
【2】startActivityForResult与startActivity区别(见上)
【3】Parcelable序列化
Android中实现序列化有两个选择:一是实现Serializable接口 (是JavaSE本身就支持的),一是实现Parcelable接口(Android特有功能,效率比实现Serializable接口高效,可用于 Intent数据传递,也可以用于进程间通信(IPC))。实现Serializable接口非常简单,声明一下就可以了,而实现Parcelable接
口稍微复杂一些(实例化静态内部对象CREATOR实现接口Parcelable.Creator),但效率更高,推荐用这种方法提高性能。
注:Android中Intent传递对象有两种方法:一是 Bundle.putSerializable(Key,Object),另一种是Bundle.putParcelable(Key,Object)。 当然这些Object是有一定的条件的,前者是实现了Serializable接口,而后者是实现了Parcelable接口。
选择序列化方法的原则
1)在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。
2)Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
3)Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。
【4】Json解析
json一共有两种数据结构,一种是以 (key/value)对形式存在的无序的jsonObject对象,一个对象以“{”(左花括号)开始,“}”(右花括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称/值’ 对”之间使用“,”(逗号)分隔;
另一种数据格式就是有序的value的集合,这种形式被称为是jsonArray,数组是值(value)的有序集合。一个数组以“[”(左中括号)开始,“]”(右中括号)结束。值之间使用“,”(逗号)分隔。
详细的网上有很多,推荐一篇json
【5】Xutil框架
【6】ListView的优化(见上)
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:android
原文地址:http://blog.csdn.net/xsf50717/article/details/47206939