android 仿QQ界面
目录(?)[+]
- 一底部控制栏
- 二顶部控制栏
- 三总控中心Activity和Fragment
最近反复研究日常经典必用的几个android app,从主界面带来的交互方式入手进行分析,我将其大致分为三类。今天记录第一种方式,即主界面下面有几个tab页,最上端是标题栏,tab页和tab页之间不是通过滑动切换的,而是通过点击切换tab页。早期这种架构一直是使用tabhost+activitygroup来使用,随着fragment的出现及google官方也大力推荐使用fragment,后者大有代替前者之势。本文也使用fragment进行搭建,标题中的“经典”指这种交互经典,非本文的代码框架结构,欢迎大家提出指出不足,帮助完善。文中的fragment部分参考了郭神的博文(链接1 链接2 链接3),代码也是在郭神代码基础上加入了自己对框架的理解。
再次重申下这种主界面交互的特点:1,多个tab,不能滑动切换只能点击切换;2,上有标题栏。这种模式也是目前app中使用最多的。如qq、百度云盘、招商银行、微博、支付宝。几个月前支付宝还是能滑动切换的,后来取消了。视图如下:
下面本文从底部控制栏、顶部控制栏及中间的内容显示载体fragment三部分叙述。
一、底部控制栏
底部控制栏里每个控件都不是单一基础控件,上面是图片、下面是文字,右上角是红点,当有更新时红点显示,否则隐藏。另外像qq的右上角还能显示未读消息的个数,我的参考链接里是通过大量的layout一点一点搭出来的,这样的好处是方便控制比较直观,另外是可以利用Linearlayout里的layout_weight这个属性,让底部的这些item均匀分布,缺点是代码上有很多重复,维护起来不方便。既然是整理app的通用模板框架,因此我将每个item视为一个对象,然后将其放在底部就ok了。本代码里只封装了上面是图片下面是文字,右上角的红点么有封装进来。
ImageText.java就作了这样一件事:
- <SPANstyle="FONT-FAMILY:ComicSansMS;FONT-SIZE:18px">packageorg.yanzi.ui;
- importorg.yanzi.constant.Constant;
- importandroid.content.Context;
- importandroid.graphics.Color;
- importandroid.util.AttributeSet;
- importandroid.view.LayoutInflater;
- importandroid.view.MotionEvent;
- importandroid.view.View;
- importandroid.view.ViewGroup;
- importandroid.widget.ImageView;
- importandroid.widget.LinearLayout;
- importandroid.widget.TextView;
- importcom.example.fragmentproject.R;
- publicclassImageTextextendsLinearLayout{
- privateContextmContext=null;
- privateImageViewmImageView=null;
- privateTextViewmTextView=null;
- privatefinalstaticintDEFAULT_IMAGE_WIDTH=64;
- privatefinalstaticintDEFAULT_IMAGE_HEIGHT=64;
- privateintCHECKED_COLOR=Color.rgb(29,118,199);//选中蓝色
- privateintUNCHECKED_COLOR=Color.GRAY;//自然灰色
- publicImageText(Contextcontext){
- super(context);
- //TODOAuto-generatedconstructorstub
- mContext=context;
- }
- publicImageText(Contextcontext,AttributeSetattrs){
- super(context,attrs);
- //TODOAuto-generatedconstructorstub
- mContext=context;
- LayoutInflaterinflater=(LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- ViewparentView=inflater.inflate(R.layout.image_text_layout,this,true);
- mImageView=(ImageView)findViewById(R.id.image_iamge_text);
- mTextView=(TextView)findViewById(R.id.text_iamge_text);
- }
- publicvoidsetImage(intid){
- if(mImageView!=null){
- mImageView.setImageResource(id);
- setImageSize(DEFAULT_IMAGE_WIDTH,DEFAULT_IMAGE_HEIGHT);
- }
- }
- publicvoidsetText(Strings){
- if(mTextView!=null){
- mTextView.setText(s);
- mTextView.setTextColor(UNCHECKED_COLOR);
- }
- }
- @Override
- publicbooleanonInterceptTouchEvent(MotionEventev){
- //TODOAuto-generatedmethodstub
- returntrue;
- }
- privatevoidsetImageSize(intw,inth){
- if(mImageView!=null){
- ViewGroup.LayoutParamsparams=mImageView.getLayoutParams();
- params.width=w;
- params.height=h;
- mImageView.setLayoutParams(params);
- }
- }
- publicvoidsetChecked(intitemID){
- if(mTextView!=null){
- mTextView.setTextColor(CHECKED_COLOR);
- }
- intcheckDrawableId=-1;
- switch(itemID){
- caseConstant.BTN_FLAG_MESSAGE:
- checkDrawableId=R.drawable.message_selected;
- break;
- caseConstant.BTN_FLAG_CONTACTS:
- checkDrawableId=R.drawable.contacts_selected;
- break;
- caseConstant.BTN_FLAG_NEWS:
- checkDrawableId=R.drawable.news_selected;
- break;
- caseConstant.BTN_FLAG_SETTING:
- checkDrawableId=R.drawable.setting_selected;
- break;
- default:break;
- }
- if(mImageView!=null){
- mImageView.setImageResource(checkDrawableId);
- }
- }
- }
- </SPAN>
<span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.ui;import org.yanzi.constant.Constant;import android.content.Context;import android.graphics.Color;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;import com.example.fragmentproject.R;public class ImageText extends LinearLayout{private Context mContext = null;private ImageView mImageView = null;private TextView mTextView = null;private final static int DEFAULT_IMAGE_WIDTH = 64;private final static int DEFAULT_IMAGE_HEIGHT = 64;private int CHECKED_COLOR = Color.rgb(29, 118, 199); //选中蓝色private int UNCHECKED_COLOR = Color.GRAY; //自然灰色public ImageText(Context context) {super(context);// TODO Auto-generated constructor stubmContext = context;}public ImageText(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stubmContext = context;LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);View parentView = inflater.inflate(R.layout.image_text_layout, this, true);mImageView = (ImageView)findViewById(R.id.image_iamge_text);mTextView = (TextView)findViewById(R.id.text_iamge_text);}public void setImage(int id){if(mImageView != null){mImageView.setImageResource(id);setImageSize(DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT);}}public void setText(String s){if(mTextView != null){mTextView.setText(s);mTextView.setTextColor(UNCHECKED_COLOR);}}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {// TODO Auto-generated method stubreturn true;}private void setImageSize(int w, int h){if(mImageView != null){ViewGroup.LayoutParams params = mImageView.getLayoutParams();params.width = w;params.height = h;mImageView.setLayoutParams(params);}}public void setChecked(int itemID){if(mTextView != null){mTextView.setTextColor(CHECKED_COLOR);}int checkDrawableId = -1;switch (itemID){case Constant.BTN_FLAG_MESSAGE:checkDrawableId = R.drawable.message_selected;break;case Constant.BTN_FLAG_CONTACTS:checkDrawableId = R.drawable.contacts_selected;break;case Constant.BTN_FLAG_NEWS:checkDrawableId = R.drawable.news_selected;break;case Constant.BTN_FLAG_SETTING:checkDrawableId = R.drawable.setting_selected;break;default:break;}if(mImageView != null){mImageView.setImageResource(checkDrawableId);}}}</span>
对应的布局: [html] view plain copy print ?
- <SPANstyle="FONT-FAMILY:ComicSansMS;FONT-SIZE:18px"><?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <ImageView
- android:id="@+id/image_iamge_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"/>
- <TextView
- android:id="@+id/text_iamge_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"/>
- </LinearLayout></SPAN>
<span style="font-family:Comic Sans MS;font-size:18px;"><?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:orientation="vertical" > <ImageView android:id="@+id/image_iamge_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" /> <TextView android:id="@+id/text_iamge_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" /></LinearLayout></span>
代码里用到了Constant.java,这里面放的都是常量: [java] view plain copy print ?
- <SPANstyle="FONT-FAMILY:ComicSansMS;FONT-SIZE:18px">packageorg.yanzi.constant;
- publicclassConstant{
- //Btn的标识
- publicstaticfinalintBTN_FLAG_MESSAGE=0x01;
- publicstaticfinalintBTN_FLAG_CONTACTS=0x01<<1;
- publicstaticfinalintBTN_FLAG_NEWS=0x01<<2;
- publicstaticfinalintBTN_FLAG_SETTING=0x01<<3;
- //Fragment的标识
- publicstaticfinalStringFRAGMENT_FLAG_MESSAGE="消息";
- publicstaticfinalStringFRAGMENT_FLAG_CONTACTS="联系人";
- publicstaticfinalStringFRAGMENT_FLAG_NEWS="新闻";
- publicstaticfinalStringFRAGMENT_FLAG_SETTING="设置";
- publicstaticfinalStringFRAGMENT_FLAG_SIMPLE="simple";
- }
- </SPAN>
<span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.constant;public class Constant {//Btn的标识public static final int BTN_FLAG_MESSAGE = 0x01;public static final int BTN_FLAG_CONTACTS = 0x01 << 1;public static final int BTN_FLAG_NEWS = 0x01 << 2;public static final int BTN_FLAG_SETTING = 0x01 << 3;//Fragment的标识public static final String FRAGMENT_FLAG_MESSAGE = "消息"; public static final String FRAGMENT_FLAG_CONTACTS = "联系人"; public static final String FRAGMENT_FLAG_NEWS = "新闻"; public static final String FRAGMENT_FLAG_SETTING = "设置"; public static final String FRAGMENT_FLAG_SIMPLE = "simple"; }</span>第一排是复合Button的标识,下面的string类型的是将来创建fragment的标识。
完成了ImageText之后,下面就是将4个这样的控件放到一个布局里。为了控制方便,我们将底部栏抽象为一个对象BottomControlPanel.java,这样在维护底部栏相关内容时直接找他就行了。BottomControlPanel继承自RelativeLayout,先来看它的布局:
bottom_panel_layout.xml
[html] view plain copy print ?- <SPANstyle="FONT-FAMILY:ComicSansMS;FONT-SIZE:18px"><?xmlversion="1.0"encoding="utf-8"?>
- <org.yanzi.ui.BottomControlPanelxmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:layout_alignParentBottom="true"
- android:gravity="center_vertical"
- android:paddingLeft="20dp"
- android:paddingRight="20dp">
- <org.yanzi.ui.ImageText
- android:id="@+id/btn_message"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"/>
- <org.yanzi.ui.ImageText
- android:id="@+id/btn_contacts"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/btn_message"/>
- <org.yanzi.ui.ImageText
- android:id="@+id/btn_news"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/btn_contacts"/>
- <org.yanzi.ui.ImageText
- android:id="@+id/btn_setting"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"/>
- </org.yanzi.ui.BottomControlPanel></SPAN>
<span style="font-family:Comic Sans MS;font-size:18px;"><?xml version="1.0" encoding="utf-8"?><org.yanzi.ui.BottomControlPanel xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="60dp" android:layout_alignParentBottom="true" android:gravity="center_vertical" android:paddingLeft="20dp" android:paddingRight="20dp" > <org.yanzi.ui.ImageText android:id="@+id/btn_message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" /> <org.yanzi.ui.ImageText android:id="@+id/btn_contacts" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@id/btn_message" /> <org.yanzi.ui.ImageText android:id="@+id/btn_news" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@id/btn_contacts" /> <org.yanzi.ui.ImageText android:id="@+id/btn_setting" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" /></org.yanzi.ui.BottomControlPanel></span>对应的java文件:
BottomControlPanel.java
[java] view plain copy print ?- <SPANstyle="FONT-FAMILY:ComicSansMS;FONT-SIZE:18px">packageorg.yanzi.ui;
- importjava.util.ArrayList;
- importjava.util.List;
- importorg.yanzi.constant.Constant;
- importandroid.content.Context;
- importandroid.graphics.Color;
- importandroid.util.AttributeSet;
- importandroid.util.Log;
- importandroid.view.View;
- importandroid.widget.RelativeLayout;
- importcom.example.fragmentproject.R;
- publicclassBottomControlPanelextendsRelativeLayoutimplementsView.OnClickListener{
- privateContextmContext;
- privateImageTextmMsgBtn=null;
- privateImageTextmContactsBtn=null;
- privateImageTextmNewsBtn=null;
- privateImageTextmSettingBtn=null;
- privateintDEFALUT_BACKGROUND_COLOR=Color.rgb(243,243,243);//Color.rgb(192,192,192)
- privateBottomPanelCallbackmBottomCallback=null;
- privateList<ImageText>viewList=newArrayList<ImageText>();
- publicinterfaceBottomPanelCallback{
- publicvoidonBottomPanelClick(intitemId);
- }
- publicBottomControlPanel(Contextcontext,AttributeSetattrs){
- super(context,attrs);
- //TODOAuto-generatedconstructorstub
- }
- @Override
- protectedvoidonFinishInflate(){
- //TODOAuto-generatedmethodstub
- mMsgBtn=(ImageText)findViewById(R.id.btn_message);
- mContactsBtn=(ImageText)findViewById(R.id.btn_contacts);
- mNewsBtn=(ImageText)findViewById(R.id.btn_news);
- mSettingBtn=(ImageText)findViewById(R.id.btn_setting);
- setBackgroundColor(DEFALUT_BACKGROUND_COLOR);
- viewList.add(mMsgBtn);
- viewList.add(mContactsBtn);
- viewList.add(mNewsBtn);
- viewList.add(mSettingBtn);
- }
- publicvoidinitBottomPanel(){
- if(mMsgBtn!=null){
- mMsgBtn.setImage(R.drawable.message_unselected);
- mMsgBtn.setText("消息");
- }
- if(mContactsBtn!=null){
- mContactsBtn.setImage(R.drawable.contacts_unselected);
- mContactsBtn.setText("联系人");
- }
- if(mNewsBtn!=null){
- mNewsBtn.setImage(R.drawable.news_unselected);
- mNewsBtn.setText("新闻");
- }
- if(mSettingBtn!=null){
- mSettingBtn.setImage(R.drawable.setting_unselected);
- mSettingBtn.setText("设置");
- }
- setBtnListener();
- }
- privatevoidsetBtnListener(){
- intnum=this.getChildCount();
- for(inti=0;i<num;i++){
- Viewv=getChildAt(i);
- if(v!=null){
- v.setOnClickListener(this);
- }
- }
- }
- publicvoidsetBottomCallback(BottomPanelCallbackbottomCallback){
- mBottomCallback=bottomCallback;
- }
- @Override
- publicvoidonClick(Viewv){
- //TODOAuto-generatedmethodstub
- initBottomPanel();
- intindex=-1;
- switch(v.getId()){
- caseR.id.btn_message:
- index=Constant.BTN_FLAG_MESSAGE;
- mMsgBtn.setChecked(Constant.BTN_FLAG_MESSAGE);
- break;
- caseR.id.btn_contacts:
- index=Constant.BTN_FLAG_CONTACTS;
- mContactsBtn.setChecked(Constant.BTN_FLAG_CONTACTS);
- break;
- caseR.id.btn_news:
- index=Constant.BTN_FLAG_NEWS;
- mNewsBtn.setChecked(Constant.BTN_FLAG_NEWS);
- break;
- caseR.id.btn_setting:
- index=Constant.BTN_FLAG_SETTING;
- mSettingBtn.setChecked(Constant.BTN_FLAG_SETTING);
- break;
- default:break;
- }
- if(mBottomCallback!=null){
- mBottomCallback.onBottomPanelClick(index);
- }
- }
- publicvoiddefaultBtnChecked(){
- if(mMsgBtn!=null){
- mMsgBtn.setChecked(Constant.BTN_FLAG_MESSAGE);
- }
- }
- @Override
- protectedvoidonLayout(booleanchanged,intleft,inttop,intright,intbottom){
- //TODOAuto-generatedmethodstub
- super.onLayout(changed,left,top,right,bottom);
- layoutItems(left,top,right,bottom);
- }
- /**最左边和最右边的view由母布局的padding进行控制位置。这里需对第2、3个view的位置重新设置
- *@paramleft
- *@paramtop
- *@paramright
- *@parambottom
- */
- privatevoidlayoutItems(intleft,inttop,intright,intbottom){
- intn=getChildCount();
- if(n==0){
- return;
- }
- intpaddingLeft=getPaddingLeft();
- intpaddingRight=getPaddingRight();
- Log.i("yanguoqi","paddingLeft="+paddingLeft+"paddingRight="+paddingRight);
- intwidth=right-left;
- intheight=bottom-top;
- Log.i("yanguoqi","width="+width+"height="+height);
- intallViewWidth=0;
- for(inti=0;i<n;i++){
- Viewv=getChildAt(i);
- Log.i("yanguoqi","v.getWidth()="+v.getWidth());
- allViewWidth+=v.getWidth();
- }
- intblankWidth=(width-allViewWidth-paddingLeft-paddingRight)/(n-1);
- Log.i("yanguoqi","blankV="+blankWidth);
- LayoutParamsparams1=(LayoutParams)viewList.get(1).getLayoutParams();
- params1.leftMargin=blankWidth;
- viewList.get(1).setLayoutParams(params1);
- LayoutParamsparams2=(LayoutParams)viewList.get(2).getLayoutParams();
- params2.leftMargin=blankWidth;
- viewList.get(2).setLayoutParams(params2);
- }
- }
- </SPAN>
<span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.ui;import java.util.ArrayList;import java.util.List;import org.yanzi.constant.Constant;import android.content.Context;import android.graphics.Color;import android.util.AttributeSet;import android.util.Log;import android.view.View;import android.widget.RelativeLayout;import com.example.fragmentproject.R;public class BottomControlPanel extends RelativeLayout implements View.OnClickListener {private Context mContext;private ImageText mMsgBtn = null;private ImageText mContactsBtn = null;private ImageText mNewsBtn = null;private ImageText mSettingBtn = null;private int DEFALUT_BACKGROUND_COLOR = Color.rgb(243, 243, 243); //Color.rgb(192, 192, 192)private BottomPanelCallback mBottomCallback = null;private List<ImageText> viewList = new ArrayList<ImageText>();public interface BottomPanelCallback{public void onBottomPanelClick(int itemId);}public BottomControlPanel(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stub}@Overrideprotected void onFinishInflate() {// TODO Auto-generated method stubmMsgBtn = (ImageText)findViewById(R.id.btn_message);mContactsBtn = (ImageText)findViewById(R.id.btn_contacts);mNewsBtn = (ImageText)findViewById(R.id.btn_news);mSettingBtn = (ImageText)findViewById(R.id.btn_setting);setBackgroundColor(DEFALUT_BACKGROUND_COLOR);viewList.add(mMsgBtn);viewList.add(mContactsBtn);viewList.add(mNewsBtn);viewList.add(mSettingBtn);}public void initBottomPanel(){if(mMsgBtn != null){mMsgBtn.setImage(R.drawable.message_unselected);mMsgBtn.setText("消息");}if(mContactsBtn != null){mContactsBtn.setImage(R.drawable.contacts_unselected);mContactsBtn.setText("联系人");}if(mNewsBtn != null){mNewsBtn.setImage(R.drawable.news_unselected);mNewsBtn.setText("新闻");}if(mSettingBtn != null){mSettingBtn.setImage(R.drawable.setting_unselected);mSettingBtn.setText("设置");}setBtnListener();}private void setBtnListener(){int num = this.getChildCount();for(int i = 0; i < num; i++){View v = getChildAt(i);if(v != null){v.setOnClickListener(this);}}}public void setBottomCallback(BottomPanelCallback bottomCallback){mBottomCallback = bottomCallback;}@Overridepublic void onClick(View v) {// TODO Auto-generated method stubinitBottomPanel();int index = -1;switch(v.getId()){case R.id.btn_message:index = Constant.BTN_FLAG_MESSAGE;mMsgBtn.setChecked(Constant.BTN_FLAG_MESSAGE);break;case R.id.btn_contacts:index = Constant.BTN_FLAG_CONTACTS;mContactsBtn.setChecked(Constant.BTN_FLAG_CONTACTS);break;case R.id.btn_news:index = Constant.BTN_FLAG_NEWS;mNewsBtn.setChecked(Constant.BTN_FLAG_NEWS);break;case R.id.btn_setting:index = Constant.BTN_FLAG_SETTING;mSettingBtn.setChecked(Constant.BTN_FLAG_SETTING);break;default:break;}if(mBottomCallback != null){mBottomCallback.onBottomPanelClick(index);}}public void defaultBtnChecked(){if(mMsgBtn != null){mMsgBtn.setChecked(Constant.BTN_FLAG_MESSAGE);}}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {// TODO Auto-generated method stubsuper.onLayout(changed, left, top, right, bottom);layoutItems(left, top, right, bottom);}/**最左边和最右边的view由母布局的padding进行控制位置。这里需对第2、3个view的位置重新设置 * @param left * @param top * @param right * @param bottom */private void layoutItems(int left, int top, int right, int bottom){int n = getChildCount();if(n == 0){return;}int paddingLeft = getPaddingLeft();int paddingRight = getPaddingRight();Log.i("yanguoqi", "paddingLeft = " + paddingLeft + " paddingRight = " + paddingRight);int width = right - left;int height = bottom - top;Log.i("yanguoqi", "width = " + width + " height = " + height);int allViewWidth = 0;for(int i = 0; i< n; i++){View v = getChildAt(i);Log.i("yanguoqi", "v.getWidth() = " + v.getWidth());allViewWidth += v.getWidth();}int blankWidth = (width - allViewWidth - paddingLeft - paddingRight) / (n - 1);Log.i("yanguoqi", "blankV = " + blankWidth );LayoutParams params1 = (LayoutParams) viewList.get(1).getLayoutParams();params1.leftMargin = blankWidth;viewList.get(1).setLayoutParams(params1);LayoutParams params2 = (LayoutParams) viewList.get(2).getLayoutParams();params2.leftMargin = blankWidth;viewList.get(2).setLayoutParams(params2);}}</span>在onFinishInflate()函数里实例化里面的子元素,在initBottomPanel()里设置每个孩子的图片和文字、监听.onLayout()里对中间的2个孩子的位置进行调整,使其均匀分布,见我的前文。这个BottomControlPanel实现了View.OnClickListener接口,在onClick()里通过id来判断用户点击了哪一个孩子。判断出来后需要做两件事,一是对这个被点击的对象进行处理,如字体颜色、图片资源的变化,右上角小红点的隐藏等等。另一方面,BottomControlPanel要告诉将来它的主人,也就是Activity到底是点了哪个,通知Activity去切换fragment。可以看到,activity类似个总控中心,BottomControlPanel管理属于它的ImageText,同时上报Activity。Activity知道消息后再切换fragment,每个fragment都有自己的事务逻辑。
这里定义了
public interface BottomPanelCallback{
public void onBottomPanelClick(int itemId);
}这个接口,通过传递Id来通知Activity。defaultBtnChecked()函数是apk初次打开后,默认切换到第一个消息fragment上。
这里有个地方需要注意,就是虽然ImageText和BottomControlPanel都是自定义控件,但两者在方式上是有区别的。在ImageText的构造函数里通过inflater将布局加载进来,它对应的布局是个普通的布局。而BottomControlPanel对应的布局文件里,直接使用了定义的BottomControlPanel,在onFinishInflate函数里实例化孩子View。前者是inflate之后实例化的。在使用ImageText到一个新的母布局时是通过<org.yanzi.ui.ImageText />这种方式进行的,那么使用BottomControlPanel有何区别,请见下文介绍Activity的布局时。
二、顶部控制栏
有了底部控制栏,顶部控制栏就可以如法炮制了。这里先交代几句,虽然Android3.0 后Google推出的有ActionBar来做顶部导航栏,参见郭神的这篇博文。但我发现,本文最前面贴图的几款应用应该都没有使用ActionBar,因为它不够灵活。ActionBar使用起来什么样,大家看看微信就知道了,那个的顶部控制栏就是ActionBar做的,这个应该没跑。
通过观察,顶部控制栏除了标题居中外,在右上角通常会再放一个按钮。不是ImageView就是TextView,这里我为了方便放的是两个TextView,右侧的按钮效果可以再TextView上弄个背景来实现。
HeadControlPanel.java
- <SPANstyle="FONT-FAMILY:ComicSansMS;FONT-SIZE:18px">packageorg.yanzi.ui;
- importorg.yanzi.constant.Constant;
- importcom.example.fragmentproject.R;
- importandroid.content.Context;
- importandroid.graphics.Color;
- importandroid.util.AttributeSet;
- importandroid.widget.RelativeLayout;
- importandroid.widget.TextView;
- publicclassHeadControlPanelextendsRelativeLayout{
- privateContextmContext;
- privateTextViewmMidleTitle;
- privateTextViewmRightTitle;
- privatestaticfinalfloatmiddle_title_size=20f;
- privatestaticfinalfloatright_title_size=17f;
- privatestaticfinalintdefault_background_color=Color.rgb(23,124,202);
- publicHeadControlPanel(Contextcontext,AttributeSetattrs){
- super(context,attrs);
- //TODOAuto-generatedconstructorstub
- }
- @Override
- protectedvoidonFinishInflate(){
- //TODOAuto-generatedmethodstub
- mMidleTitle=(TextView)findViewById(R.id.midle_title);
- mRightTitle=(TextView)findViewById(R.id.right_title);
- setBackgroundColor(default_background_color);
- }
- publicvoidinitHeadPanel(){
- if(mMidleTitle!=null){
- setMiddleTitle(Constant.FRAGMENT_FLAG_MESSAGE);
- }
- }
- publicvoidsetMiddleTitle(Strings){
- mMidleTitle.setText(s);
- mMidleTitle.setTextSize(middle_title_size);
- }
- }
- </SPAN>
<span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.ui;import org.yanzi.constant.Constant;import com.example.fragmentproject.R;import android.content.Context;import android.graphics.Color;import android.util.AttributeSet;import android.widget.RelativeLayout;import android.widget.TextView;public class HeadControlPanel extends RelativeLayout {private Context mContext;private TextView mMidleTitle;private TextView mRightTitle;private static final float middle_title_size = 20f; private static final float right_title_size = 17f; private static final int default_background_color = Color.rgb(23, 124, 202);public HeadControlPanel(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stub}@Overrideprotected void onFinishInflate() {// TODO Auto-generated method stubmMidleTitle = (TextView)findViewById(R.id.midle_title);mRightTitle = (TextView)findViewById(R.id.right_title);setBackgroundColor(default_background_color);}public void initHeadPanel(){if(mMidleTitle != null){setMiddleTitle(Constant.FRAGMENT_FLAG_MESSAGE);}}public void setMiddleTitle(String s){mMidleTitle.setText(s);mMidleTitle.setTextSize(middle_title_size);}}</span>
布局文件head_panel_layout.xml [html] view plain copy print ?
- <SPANstyle="FONT-FAMILY:ComicSansMS;FONT-SIZE:18px"><?xmlversion="1.0"encoding="utf-8"?>
- <org.yanzi.ui.HeadControlPanelxmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="50dp"
- android:layout_alignParentTop="true">
- <TextView
- android:id="@+id/midle_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:textColor="@android:color/white"/>
- <TextView
- android:id="@+id/right_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:textColor="@android:color/white"/>
- </org.yanzi.ui.HeadControlPanel>
- </SPAN>
<span style="font-family:Comic Sans MS;font-size:18px;"><?xml version="1.0" encoding="utf-8"?><org.yanzi.ui.HeadControlPanel xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="50dp" android:layout_alignParentTop="true"> <TextView android:id="@+id/midle_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textColor="@android:color/white"/> <TextView android:id="@+id/right_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:textColor="@android:color/white"/> </org.yanzi.ui.HeadControlPanel></span>
三、总控中心Activity和Fragment
先交代下Fragment的使用大致分两种,一种是将Fragment作为一个View写死在布局中,布局里使用android:name来告诉它对应的是哪个实体Fragment。这种添加fragment的方式不能delete和replace掉。另一种是通过获得activity的fragmentmanager和fragmentTransaction和进行动态的添加。这种方式更加灵活,一般使用此种方法。
先看Activity的布局activity_main.xml:
[html] view plain copy print ?- <SPANstyle="FONT-FAMILY:ComicSansMS;FONT-SIZE:18px"><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/root_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="org.yanzi.fragmentproject.MainActivity">
- <include
- android:id="@+id/bottom_layout"
- layout="@layout/bottom_panel_layout"/>
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:layout_above="@id/bottom_layout"
- android:background="#FFE7E7E7"/>
- <include
- android:id="@+id/head_layout"
- layout="@layout/head_panel_layout"/>
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:layout_below="@id/head_layout"
- android:background="#FFE7E7E7"/>
- <FrameLayout
- android:id="@+id/fragment_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/head_layout"
- android:layout_above="@id/bottom_layout">
- </FrameLayout>
- </RelativeLayout></SPAN>
<span style="font-family:Comic Sans MS;font-size:18px;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/root_layout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="org.yanzi.fragmentproject.MainActivity" > <include android:id="@+id/bottom_layout" layout="@layout/bottom_panel_layout" /> <View android:layout_width="match_parent" android:layout_height="1dip" android:layout_above="@id/bottom_layout" android:background="#FFE7E7E7" /> <include android:id="@+id/head_layout" layout="@layout/head_panel_layout" /> <View android:layout_width="match_parent" android:layout_height="1dip" android:layout_below="@id/head_layout" android:background="#FFE7E7E7" /> <FrameLayout android:id="@+id/fragment_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/head_layout" android:layout_above="@id/bottom_layout" > </FrameLayout></RelativeLayout></span>
注意看这里是通过include的方式把刚才自定义的上下panel加过来,而不能直接用<org.yanzi.ui.BottomControlPanel />这种方式直接加载。当然如果也模仿ImageText的构造方式,也是可以这样用的。关于include方式的使用有几个注意事项,就是最好让它的母布局是RelativeLayout,否则的话很难控制include进来的布局的位置。另外,include布局的位置一定要写在include之前,如底部面板在最底部,android:layout_alignParentBottom="true"这句话是在bottom_panel_layout.xml里写的,如果写在activity_main.xml里就是无效的,这着实是个蛋疼的问题。再就是include后设置的id会覆盖掉以前的,所以这里只在include的时候设置id。其中的两个View是分割线。整体是按照底部栏、上部栏、中间Fragment的容器来放置的。
在放Fragment的时候需要注意,究竟是否要将顶部控制栏放到各自的fragment里合适还是放到Activity里合适要看具体情况,如果顶部栏里多是显示标题这种功能或少量的点击事件,应该放到Activity里,即顶部栏的事务逻辑和当前fragment的事务逻辑耦合的不是很紧。举个例子,比如微信的顶部栏,不管你处在哪个Tab页(聊天、发现、通讯录),点击顶部栏里的按钮都呈现出同样的内容。但反过来讲,如果顶部栏里的事务逻辑和fragment耦合很紧,即在不同的fragment,顶部栏呈现的内容都不一样,且点击后处理的事务也和当前fragment紧密联系一起,那就应该一个fragment配套一个顶部栏,方便控制。本文是将两者分开的。所以让fragment的容器在顶部栏之下,底部栏之上,不这样写的话,就会遮挡。
<FrameLayout
android:id="@+id/fragment_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/head_layout"
android:layout_above="@id/bottom_layout" >
</FrameLayout>
MainActivity.java代码:
- <SPANstyle="FONT-FAMILY:ComicSansMS;FONT-SIZE:18px">packageorg.yanzi.activity;
- importorg.yanzi.constant.Constant;
- importorg.yanzi.fragment.BaseFragment;
- importorg.yanzi.fragment.ContactsFragment;
- importorg.yanzi.fragment.MessageFragment;
- importorg.yanzi.fragment.NewsFragment;
- importorg.yanzi.fragment.SettingFragment;
- importorg.yanzi.ui.BottomControlPanel;
- importorg.yanzi.ui.BottomControlPanel.BottomPanelCallback;
- importorg.yanzi.ui.HeadControlPanel;
- importandroid.app.Activity;
- importandroid.app.Fragment;
- importandroid.app.FragmentManager;
- importandroid.app.FragmentTransaction;
- importandroid.os.Bundle;
- importandroid.text.TextUtils;
- importandroid.util.Log;
- importandroid.view.Menu;
- importandroid.widget.Toast;
- importcom.example.fragmentproject.R;
- publicclassMainActivityextendsActivityimplementsBottomPanelCallback{
- BottomControlPanelbottomPanel=null;
- HeadControlPanelheadPanel=null;
- privateFragmentManagerfragmentManager=null;
- privateFragmentTransactionfragmentTransaction=null;
- /*privateMessageFragmentmessageFragment;
- privateContactsFragmentcontactsFragment;
- privateNewsFragmentnewsFragment;
- privateSettingFragmentsettingFragment;*/
- publicstaticStringcurrFragTag="";
- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- initUI();
- fragmentManager=getFragmentManager();
- setDefaultFirstFragment(Constant.FRAGMENT_FLAG_MESSAGE);
- }
- @Override
- publicbooleanonCreateOptionsMenu(Menumenu){
- //Inflatethemenu;thisaddsitemstotheactionbarifitispresent.
- getMenuInflater().inflate(R.menu.main,menu);
- returntrue;
- }
- privatevoidinitUI(){
- bottomPanel=(BottomControlPanel)findViewById(R.id.bottom_layout);
- if(bottomPanel!=null){
- bottomPanel.initBottomPanel();
- bottomPanel.setBottomCallback(this);
- }
- headPanel=(HeadControlPanel)findViewById(R.id.head_layout);
- if(headPanel!=null){
- headPanel.initHeadPanel();
- }
- }
- /*处理BottomControlPanel的回调
- *@seeorg.yanzi.ui.BottomControlPanel.BottomPanelCallback#onBottomPanelClick(int)
- */
- @Override
- publicvoidonBottomPanelClick(intitemId){
- //TODOAuto-generatedmethodstub
- Stringtag="";
- if((itemId&Constant.BTN_FLAG_MESSAGE)!=0){
- tag=Constant.FRAGMENT_FLAG_MESSAGE;
- }elseif((itemId&Constant.BTN_FLAG_CONTACTS)!=0){
- tag=Constant.FRAGMENT_FLAG_CONTACTS;
- }elseif((itemId&Constant.BTN_FLAG_NEWS)!=0){
- tag=Constant.FRAGMENT_FLAG_NEWS;
- }elseif((itemId&Constant.BTN_FLAG_SETTING)!=0){
- tag=Constant.FRAGMENT_FLAG_SETTING;
- }
- setTabSelection(tag);//切换Fragment
- headPanel.setMiddleTitle(tag);//切换标题
- }
- privatevoidsetDefaultFirstFragment(Stringtag){
- Log.i("yan","setDefaultFirstFragmententer...currFragTag="+currFragTag);
- setTabSelection(tag);
- bottomPanel.defaultBtnChecked();
- Log.i("yan","setDefaultFirstFragmentexit...");
- }
- privatevoidcommitTransactions(Stringtag){
- if(fragmentTransaction!=null&&!fragmentTransaction.isEmpty()){
- fragmentTransaction.commit();
- currFragTag=tag;
- fragmentTransaction=null;
- }
- }
- privateFragmentTransactionensureTransaction(){
- if(fragmentTransaction==null){
- fragmentTransaction=fragmentManager.beginTransaction();
- fragmentTransaction
- .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
- }
- returnfragmentTransaction;
- }
- privatevoidattachFragment(intlayout,Fragmentf,Stringtag){
- if(f!=null){
- if(f.isDetached()){
- ensureTransaction();
- fragmentTransaction.attach(f);
- }elseif(!f.isAdded()){
- ensureTransaction();
- fragmentTransaction.add(layout,f,tag);
- }
- }
- }
- privateFragmentgetFragment(Stringtag){
- Fragmentf=fragmentManager.findFragmentByTag(tag);
- if(f==null){
- Toast.makeText(getApplicationContext(),"fragment=nulltag="+tag,Toast.LENGTH_SHORT).show();
- f=BaseFragment.newInstance(getApplicationContext(),tag);
- }
- returnf;
- }
- privatevoiddetachFragment(Fragmentf){
- if(f!=null&&!f.isDetached()){
- ensureTransaction();
- fragmentTransaction.detach(f);
- }
- }
- /**切换fragment
- *@paramtag
- */
- privatevoidswitchFragment(Stringtag){
- if(TextUtils.equals(tag,currFragTag)){
- return;
- }
- //把上一个fragmentdetach掉
- if(currFragTag!=null&&!currFragTag.equals("")){
- detachFragment(getFragment(currFragTag));
- }
- attachFragment(R.id.fragment_content,getFragment(tag),tag);
- commitTransactions(tag);
- }
- /**设置选中的Tag
- *@paramtag
- */
- publicvoidsetTabSelection(Stringtag){
- //开启一个Fragment事务
- fragmentTransaction=fragmentManager.beginTransaction();
- /*if(TextUtils.equals(tag,Constant.FRAGMENT_FLAG_MESSAGE)){
- if(messageFragment==null){
- messageFragment=newMessageFragment();
- }
- }elseif(TextUtils.equals(tag,Constant.FRAGMENT_FLAG_CONTACTS)){
- if(contactsFragment==null){
- contactsFragment=newContactsFragment();
- }
- }elseif(TextUtils.equals(tag,Constant.FRAGMENT_FLAG_NEWS)){
- if(newsFragment==null){
- newsFragment=newNewsFragment();
- }
- }elseif(TextUtils.equals(tag,Constant.FRAGMENT_FLAG_SETTING)){
- if(settingFragment==null){
- settingFragment=newSettingFragment();
- }
- }elseif(TextUtils.equals(tag,Constant.FRAGMENT_FLAG_SIMPLE)){
- if(simpleFragment==null){
- simpleFragment=newSimpleFragment();
- }
- }*/
- switchFragment(tag);
- }
- @Override
- protectedvoidonStop(){
- //TODOAuto-generatedmethodstub
- super.onStop();
- currFragTag="";
- }
- @Override
- protectedvoidonSaveInstanceState(BundleoutState){
- //TODOAuto-generatedmethodstub
- }
- }
- </SPAN>
<span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.activity;import org.yanzi.constant.Constant;import org.yanzi.fragment.BaseFragment;import org.yanzi.fragment.ContactsFragment;import org.yanzi.fragment.MessageFragment;import org.yanzi.fragment.NewsFragment;import org.yanzi.fragment.SettingFragment;import org.yanzi.ui.BottomControlPanel;import org.yanzi.ui.BottomControlPanel.BottomPanelCallback;import org.yanzi.ui.HeadControlPanel;import android.app.Activity;import android.app.Fragment;import android.app.FragmentManager;import android.app.FragmentTransaction;import android.os.Bundle;import android.text.TextUtils;import android.util.Log;import android.view.Menu;import android.widget.Toast;import com.example.fragmentproject.R;public class MainActivity extends Activity implements BottomPanelCallback {BottomControlPanel bottomPanel = null;HeadControlPanel headPanel = null;private FragmentManager fragmentManager = null;private FragmentTransaction fragmentTransaction = null;/*private MessageFragment messageFragment;private ContactsFragment contactsFragment;private NewsFragment newsFragment;private SettingFragment settingFragment;*/public static String currFragTag = "";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initUI();fragmentManager = getFragmentManager();setDefaultFirstFragment(Constant.FRAGMENT_FLAG_MESSAGE);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}private void initUI(){bottomPanel = (BottomControlPanel)findViewById(R.id.bottom_layout);if(bottomPanel != null){bottomPanel.initBottomPanel();bottomPanel.setBottomCallback(this);}headPanel = (HeadControlPanel)findViewById(R.id.head_layout);if(headPanel != null){headPanel.initHeadPanel();}}/* 处理BottomControlPanel的回调 * @see org.yanzi.ui.BottomControlPanel.BottomPanelCallback#onBottomPanelClick(int) */@Overridepublic void onBottomPanelClick(int itemId) {// TODO Auto-generated method stubString tag = "";if((itemId & Constant.BTN_FLAG_MESSAGE) != 0){tag = Constant.FRAGMENT_FLAG_MESSAGE;}else if((itemId & Constant.BTN_FLAG_CONTACTS) != 0){tag = Constant.FRAGMENT_FLAG_CONTACTS;}else if((itemId & Constant.BTN_FLAG_NEWS) != 0){tag = Constant.FRAGMENT_FLAG_NEWS;}else if((itemId & Constant.BTN_FLAG_SETTING) != 0){tag = Constant.FRAGMENT_FLAG_SETTING;}setTabSelection(tag); //切换FragmentheadPanel.setMiddleTitle(tag);//切换标题 }private void setDefaultFirstFragment(String tag){Log.i("yan", "setDefaultFirstFragment enter... currFragTag = " + currFragTag);setTabSelection(tag);bottomPanel.defaultBtnChecked();Log.i("yan", "setDefaultFirstFragment exit...");}private void commitTransactions(String tag){if (fragmentTransaction != null && !fragmentTransaction.isEmpty()) {fragmentTransaction.commit();currFragTag = tag;fragmentTransaction = null;}}private FragmentTransaction ensureTransaction( ){if(fragmentTransaction == null){fragmentTransaction = fragmentManager.beginTransaction();fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);}return fragmentTransaction;}private void attachFragment(int layout, Fragment f, String tag){if(f != null){if(f.isDetached()){ensureTransaction();fragmentTransaction.attach(f);}else if(!f.isAdded()){ensureTransaction();fragmentTransaction.add(layout, f, tag);}}}private Fragment getFragment(String tag){Fragment f = fragmentManager.findFragmentByTag(tag);if(f == null){Toast.makeText(getApplicationContext(), "fragment = null tag = " + tag, Toast.LENGTH_SHORT).show();f = BaseFragment.newInstance(getApplicationContext(), tag);}return f;}private void detachFragment(Fragment f){if(f != null && !f.isDetached()){ensureTransaction();fragmentTransaction.detach(f);}}/**切换fragment * @param tag */private void switchFragment(String tag){if(TextUtils.equals(tag, currFragTag)){return;}//把上一个fragment detach掉 if(currFragTag != null && !currFragTag.equals("")){detachFragment(getFragment(currFragTag));}attachFragment(R.id.fragment_content, getFragment(tag), tag);commitTransactions( tag);} /**设置选中的Tag * @param tag */public void setTabSelection(String tag) {// 开启一个Fragment事务fragmentTransaction = fragmentManager.beginTransaction();/* if(TextUtils.equals(tag, Constant.FRAGMENT_FLAG_MESSAGE)){ if (messageFragment == null) {messageFragment = new MessageFragment();} }else if(TextUtils.equals(tag, Constant.FRAGMENT_FLAG_CONTACTS)){if (contactsFragment == null) {contactsFragment = new ContactsFragment();} }else if(TextUtils.equals(tag, Constant.FRAGMENT_FLAG_NEWS)){if (newsFragment == null) {newsFragment = new NewsFragment();}}else if(TextUtils.equals(tag,Constant.FRAGMENT_FLAG_SETTING)){if (settingFragment == null) {settingFragment = new SettingFragment();}}else if(TextUtils.equals(tag, Constant.FRAGMENT_FLAG_SIMPLE)){if (simpleFragment == null) {simpleFragment = new SimpleFragment();} }*/ switchFragment(tag); }@Overrideprotected void onStop() {// TODO Auto-generated method stubsuper.onStop();currFragTag = "";}@Overrideprotected void onSaveInstanceState(Bundle outState) {// TODO Auto-generated method stub}}</span>
注意这块我作了改动,不需要申明
/* private MessageFragment messageFragment;
private ContactsFragment contactsFragment;
private NewsFragment newsFragment;
private SettingFragment settingFragment;*/
这些内容,因为Fragment的生成是通过BaseFragment.newInstance()来生成的,传进去Tag生成相应的Fragment。所有的Fragment,ContactsFragment、MessageFragment、NewsFragment、SettingFragment都继承自BaseFragment,通过BaseFragment里的newInstance()接口进行实例化对应的fragment。优点是方便管理,缺点么也有,因为java继承继承一个类,不能同时继承两个类。所以如ListFragment这些,就没法同时继承了。不过好在有listview这些,也妨碍不了我们做到同样的效果。
Activity里事件的入口是在onBottomPanelClick()监听点击了谁,然后:
setTabSelection(tag); //切换Fragment
headPanel.setMiddleTitle(tag);//切换标题
先切换Fragment再切换顶部栏的标题。setTabSelection()里直接调switchFragment(),在switchFragment函数里先判断标签是否一样,一样则意外着无需切换,否则的话就先把当前Fragment找到然后detach掉,之后进到attachFragment()函数里。在这里,先判断这个fragment是不是被detach掉的,如果是的话意味着之前曾被add过,所以只需attach就ok了。否则的话,意味着这是第一次,进行add.这里记录下Fragment的声明周期:
MessageFragment正常打开:
Line 155: 01-04 11:50:46.688 E/MessageFragment( 2546): onAttach-----
Line 159: 01-04 11:50:46.688 E/MessageFragment( 2546): onCreate------
Line 161: 01-04 11:50:46.693 D/MessageFragment( 2546): onCreateView---->
Line 165: 01-04 11:50:46.694 E/MessageFragment( 2546): onActivityCreated-------
Line 169: 01-04 11:50:46.694 E/MessageFragment( 2546): onStart----->
Line 173: 01-04 11:50:46.694 E/MessageFragment( 2546): onresume---->
返回键退出:
Line 183: 01-04 11:52:26.506 E/MessageFragment( 2546): onpause
Line 259: 01-04 11:52:27.131 E/MessageFragment( 2546): onStop
Line 263: 01-04 11:52:27.132 E/MessageFragment( 2546): ondestoryView
Line 269: 01-04 11:52:27.134 E/MessageFragment( 2546): ondestory
Line 271: 01-04 11:52:27.135 D/MessageFragment( 2546): onDetach------
按home按键退出:
Line 97: 01-05 05:06:15.659 E/MessageFragment(18835): onpause
Line 215: 01-05 05:06:16.292 E/MessageFragment(18835): onStop
再次打开:
Line 81: 01-05 05:07:02.408 E/MessageFragment(18835): onStart----->
Line 85: 01-05 05:07:02.408 E/MessageFragment(18835): onresume---->
通过detach的方式切换至其他Fragment:
Line 69: 01-04 11:53:33.381 E/MessageFragment( 2546): onpause
Line 73: 01-04 11:53:33.382 E/MessageFragment( 2546): onStop
Line 77: 01-04 11:53:33.382 E/MessageFragment( 2546): ondestoryView
再次切换过来:
Line 55: 01-04 11:54:59.462 D/MessageFragment( 2546): onCreateView---->
Line 59: 01-04 11:54:59.463 E/MessageFragment( 2546): onActivityCreated-------
Line 63: 01-04 11:54:59.463 E/MessageFragment( 2546): onStart----->
Line 67: 01-04 11:54:59.464 E/MessageFragment( 2546): onresume---->
四、适配器和MessageBean
本来要连数据库的,时间原因用个简单的MessageBean代替了。一个消息分联系人头像、名字、消息正文和时间四部分组成,封装到一个MessageBean里。
MessageBean.java
- <SPANstyle="FONT-FAMILY:ComicSansMS;FONT-SIZE:18px">packageorg.yanzi.bean;
- publicclassMessageBean{
- privateintPhotoDrawableId;
- privateStringMessageName;
- privateStringMessageContent;
- privateStringMessageTime;
- publicMessageBean(){
- }
- publicMessageBean(intphotoDrawableId,StringmessageName,
- StringmessageContent,StringmessageTime){
- super();
- PhotoDrawableId=photoDrawableId;
- MessageName=messageName;
- MessageContent=messageContent;
- MessageTime=messageTime;
- }
- publicintgetPhotoDrawableId(){
- returnPhotoDrawableId;
- }
- publicvoidsetPhotoDrawableId(intmPhotoDrawableId){
- this.PhotoDrawableId=mPhotoDrawableId;
- }
- publicStringgetMessageName(){
- returnMessageName;
- }
- publicvoidsetMessageName(StringmessageName){
- MessageName=messageName;
- }
- publicStringgetMessageContent(){
- returnMessageContent;
- }
- publicvoidsetMessageContent(StringmessageContent){
- MessageContent=messageContent;
- }
- publicStringgetMessageTime(){
- returnMessageTime;
- }
- publicvoidsetMessageTime(StringmessageTime){
- MessageTime=messageTime;
- }
- @Override
- publicStringtoString(){
- return"MessageBean[mPhotoDrawableId="+PhotoDrawableId
- +",MessageName="+MessageName+",MessageContent="
- +MessageContent+",MessageTime="+MessageTime+"]";
- }
- }
- </SPAN>
<span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.bean;public class MessageBean {private int PhotoDrawableId;private String MessageName;private String MessageContent;private String MessageTime;public MessageBean(){}public MessageBean(int photoDrawableId, String messageName,String messageContent, String messageTime) {super();PhotoDrawableId = photoDrawableId;MessageName = messageName;MessageContent = messageContent;MessageTime = messageTime;}public int getPhotoDrawableId() {return PhotoDrawableId;}public void setPhotoDrawableId(int mPhotoDrawableId) {this.PhotoDrawableId = mPhotoDrawableId;}public String getMessageName() {return MessageName;}public void setMessageName(String messageName) {MessageName = messageName;}public String getMessageContent() {return MessageContent;}public void setMessageContent(String messageContent) {MessageContent = messageContent;}public String getMessageTime() {return MessageTime;}public void setMessageTime(String messageTime) {MessageTime = messageTime;}@Overridepublic String toString() {return "MessageBean [mPhotoDrawableId=" + PhotoDrawableId+ ", MessageName=" + MessageName + ", MessageContent="+ MessageContent + ", MessageTime=" + MessageTime + "]";}}</span>然后就是MessageFragment的ListView里的适配器,MessageAdapter.java [java] view plain copy print ?
- <SPANstyle="FONT-FAMILY:ComicSansMS;FONT-SIZE:18px">packageorg.yanzi.fragment.adapter;
- importjava.util.List;
- importorg.yanzi.bean.MessageBean;
- importcom.example.fragmentproject.R;
- importandroid.content.Context;
- importandroid.view.LayoutInflater;
- importandroid.view.View;
- importandroid.view.ViewGroup;
- importandroid.widget.BaseAdapter;
- importandroid.widget.ImageView;
- importandroid.widget.TextView;
- publicclassMessageAdapterextendsBaseAdapter{
- privateList<MessageBean>mListMsgBean=null;
- privateContextmContext;
- privateLayoutInflatermInflater;
- publicMessageAdapter(List<MessageBean>listMsgBean,Contextcontext){
- mListMsgBean=listMsgBean;
- mContext=context;
- mInflater=LayoutInflater.from(mContext);
- }
- @Override
- publicintgetCount(){
- //TODOAuto-generatedmethodstub
- returnmListMsgBean.size();
- }
- @Override
- publicObjectgetItem(intposition){
- //TODOAuto-generatedmethodstub
- returnmListMsgBean.get(position);
- }
- @Override
- publiclonggetItemId(intposition){
- //TODOAuto-generatedmethodstub
- returnposition;
- }
- @Override
- publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
- //TODOAuto-generatedmethodstub
- Viewv=mInflater.inflate(R.layout.message_item_layout,null);
- ImageViewimageView=(ImageView)v.findViewById(R.id.img_msg_item);
- imageView.setImageResource(mListMsgBean.get(position).getPhotoDrawableId());
- TextViewnameMsg=(TextView)v.findViewById(R.id.name_msg_item);
- nameMsg.setText(mListMsgBean.get(position).getMessageName());
- TextViewcontentMsg=(TextView)v.findViewById(R.id.content_msg_item);
- contentMsg.setText(mListMsgBean.get(position).getMessageContent());
- TextViewtimeMsg=(TextView)v.findViewById(R.id.time_msg_item);
- timeMsg.setText(mListMsgBean.get(position).getMessageTime());
- returnv;
- }
- }
- </SPAN>
<span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.fragment.adapter;import java.util.List;import org.yanzi.bean.MessageBean;import com.example.fragmentproject.R;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;public class MessageAdapter extends BaseAdapter {private List<MessageBean> mListMsgBean = null;private Context mContext;private LayoutInflater mInflater;public MessageAdapter(List<MessageBean> listMsgBean, Context context){mListMsgBean = listMsgBean;mContext = context;mInflater = LayoutInflater.from(mContext);}@Overridepublic int getCount() {// TODO Auto-generated method stubreturn mListMsgBean.size();}@Overridepublic Object getItem(int position) {// TODO Auto-generated method stubreturn mListMsgBean.get(position);}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// TODO Auto-generated method stubView v = mInflater.inflate(R.layout.message_item_layout, null);ImageView imageView = (ImageView) v.findViewById(R.id.img_msg_item);imageView.setImageResource(mListMsgBean.get(position).getPhotoDrawableId());TextView nameMsg = (TextView)v.findViewById(R.id.name_msg_item);nameMsg.setText(mListMsgBean.get(position).getMessageName());TextView contentMsg = (TextView)v.findViewById(R.id.content_msg_item);contentMsg.setText(mListMsgBean.get(position).getMessageContent());TextView timeMsg = (TextView)v.findViewById(R.id.time_msg_item);timeMsg.setText(mListMsgBean.get(position).getMessageTime());return v;}}</span>因为是示例,getView里没用ViewHolder。
最后是MessageFragment里通过对listview设置适配器,将MessageBean作为信息的提供者也填充到适配器里。
MessageFragment.java代码:
- <SPANstyle="FONT-FAMILY:ComicSansMS;FONT-SIZE:18px">packageorg.yanzi.fragment;
- importjava.util.ArrayList;
- importjava.util.List;
- importorg.yanzi.activity.MainActivity;
- importorg.yanzi.bean.MessageBean;
- importorg.yanzi.constant.Constant;
- importorg.yanzi.fragment.adapter.MessageAdapter;
- importandroid.app.Activity;
- importandroid.os.Bundle;
- importandroid.util.Log;
- importandroid.view.LayoutInflater;
- importandroid.view.View;
- importandroid.view.ViewGroup;
- importandroid.widget.AdapterView;
- importandroid.widget.Toast;
- importandroid.widget.AdapterView.OnItemClickListener;
- importandroid.widget.ListView;
- importcom.example.fragmentproject.R;
- publicclassMessageFragmentextendsBaseFragment{
- privatestaticfinalStringTAG="MessageFragment";
- privateMainActivitymMainActivity;
- privateListViewmListView;
- privateMessageAdaptermMsgAdapter;
- privateList<MessageBean>mMsgBean=newArrayList<MessageBean>();
- publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,
- BundlesavedInstanceState){
- ViewmessageLayout=inflater.inflate(R.layout.message_layout,
- container,false);
- Log.d(TAG,"onCreateView---->");
- mMainActivity=(MainActivity)getActivity();
- mFragmentManager=getActivity().getFragmentManager();
- mListView=(ListView)messageLayout.findViewById(R.id.listview_message);
- mMsgAdapter=newMessageAdapter(mMsgBean,mMainActivity);
- mListView.setAdapter(mMsgAdapter);
- mListView.setOnItemClickListener(newOnItemClickListener(){
- @Override
- publicvoidonItemClick(AdapterView<?>parent,Viewview,
- intposition,longid){
- //TODOAuto-generatedmethodstub
- Toast.makeText(mMainActivity,mMsgBean.get(position).toString(),
- Toast.LENGTH_SHORT).show();
- }
- });
- returnmessageLayout;
- }
- @Override
- publicvoidonAttach(Activityactivity){
- //TODOAuto-generatedmethodstub
- super.onAttach(activity);
- Log.e(TAG,"onAttach-----");
- }
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- //TODOAuto-generatedmethodstub
- super.onCreate(savedInstanceState);
- Log.e(TAG,"onCreate------");
- mMsgBean.add(newMessageBean(R.drawable.ic_photo_1,"张三","吃饭没?","昨天"));
- mMsgBean.add(newMessageBean(R.drawable.ic_photo_2,"李四","哈哈","昨天"));
- mMsgBean.add(newMessageBean(R.drawable.ic_photo_3,"小明","吃饭没?","昨天"));
- mMsgBean.add(newMessageBean(R.drawable.ic_photo_4,"王五","吃饭没?","昨天"));
- mMsgBean.add(newMessageBean(R.drawable.ic_photo_5,"Jack","吃饭没?","昨天"));
- mMsgBean.add(newMessageBean(R.drawable.ic_photo_6,"Jone","吃饭没?","昨天"));
- mMsgBean.add(newMessageBean(R.drawable.ic_photo_7,"Jone","吃饭没?","昨天"));
- mMsgBean.add(newMessageBean(R.drawable.ic_photo_8,"Jone","吃饭没?","昨天"));
- mMsgBean.add(newMessageBean(R.drawable.ic_photo_9,"Jone","吃饭没?","昨天"));
- }
- @Override
- publicvoidonActivityCreated(BundlesavedInstanceState){
- //TODOAuto-generatedmethodstub
- super.onActivityCreated(savedInstanceState);
- Log.e(TAG,"onActivityCreated-------");
- }
- @Override
- publicvoidonStart(){
- //TODOAuto-generatedmethodstub
- super.onStart();
- Log.e(TAG,"onStart----->");
- }
- @Override
- publicvoidonResume(){
- //TODOAuto-generatedmethodstub
- super.onResume();
- Log.e(TAG,"onresume---->");
- MainActivity.currFragTag=Constant.FRAGMENT_FLAG_MESSAGE;
- }
- @Override
- publicvoidonPause(){
- //TODOAuto-generatedmethodstub
- super.onPause();
- Log.e(TAG,"onpause");
- }
- @Override
- publicvoidonStop(){
- //TODOAuto-generatedmethodstub
- super.onStop();
- Log.e(TAG,"onStop");
- }
- @Override
- publicvoidonDestroyView(){
- //TODOAuto-generatedmethodstub
- super.onDestroyView();
- Log.e(TAG,"ondestoryView");
- }
- @Override
- publicvoidonDestroy(){
- //TODOAuto-generatedmethodstub
- super.onDestroy();
- Log.e(TAG,"ondestory");
- }
- @Override
- publicvoidonDetach(){
- //TODOAuto-generatedmethodstub
- super.onDetach();
- Log.d(TAG,"onDetach------");
- }
- }
- </SPAN>
<span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.fragment;import java.util.ArrayList;import java.util.List;import org.yanzi.activity.MainActivity;import org.yanzi.bean.MessageBean;import org.yanzi.constant.Constant;import org.yanzi.fragment.adapter.MessageAdapter;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.AdapterView;import android.widget.Toast;import android.widget.AdapterView.OnItemClickListener;import android.widget.ListView;import com.example.fragmentproject.R;public class MessageFragment extends BaseFragment {private static final String TAG = "MessageFragment";private MainActivity mMainActivity ;private ListView mListView;private MessageAdapter mMsgAdapter;private List<MessageBean> mMsgBean = new ArrayList<MessageBean>();public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View messageLayout = inflater.inflate(R.layout.message_layout,container, false);Log.d(TAG, "onCreateView---->");mMainActivity = (MainActivity) getActivity();mFragmentManager = getActivity().getFragmentManager();mListView = (ListView)messageLayout.findViewById(R.id.listview_message);mMsgAdapter = new MessageAdapter(mMsgBean, mMainActivity);mListView.setAdapter(mMsgAdapter);mListView.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {// TODO Auto-generated method stubToast.makeText(mMainActivity, mMsgBean.get(position).toString(),Toast.LENGTH_SHORT).show();}});return messageLayout;}@Overridepublic void onAttach(Activity activity) {// TODO Auto-generated method stubsuper.onAttach(activity);Log.e(TAG, "onAttach-----");}@Overridepublic void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);Log.e(TAG, "onCreate------");mMsgBean.add(new MessageBean(R.drawable.ic_photo_1, "张三", "吃饭没?", "昨天"));mMsgBean.add(new MessageBean(R.drawable.ic_photo_2, "李四", "哈哈", "昨天"));mMsgBean.add(new MessageBean(R.drawable.ic_photo_3, "小明", "吃饭没?", "昨天"));mMsgBean.add(new MessageBean(R.drawable.ic_photo_4, "王五", "吃饭没?", "昨天"));mMsgBean.add(new MessageBean(R.drawable.ic_photo_5, "Jack", "吃饭没?", "昨天"));mMsgBean.add(new MessageBean(R.drawable.ic_photo_6, "Jone", "吃饭没?", "昨天"));mMsgBean.add(new MessageBean(R.drawable.ic_photo_7, "Jone", "吃饭没?", "昨天"));mMsgBean.add(new MessageBean(R.drawable.ic_photo_8, "Jone", "吃饭没?", "昨天"));mMsgBean.add(new MessageBean(R.drawable.ic_photo_9, "Jone", "吃饭没?", "昨天"));}@Overridepublic void onActivityCreated(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onActivityCreated(savedInstanceState);Log.e(TAG, "onActivityCreated-------");}@Overridepublic void onStart() {// TODO Auto-generated method stubsuper.onStart();Log.e(TAG, "onStart----->");}@Overridepublic void onResume() {// TODO Auto-generated method stubsuper.onResume();Log.e(TAG, "onresume---->");MainActivity.currFragTag = Constant.FRAGMENT_FLAG_MESSAGE;}@Overridepublic void onPause() {// TODO Auto-generated method stubsuper.onPause();Log.e(TAG, "onpause");}@Overridepublic void onStop() {// TODO Auto-generated method stubsuper.onStop();Log.e(TAG, "onStop");}@Overridepublic void onDestroyView() {// TODO Auto-generated method stubsuper.onDestroyView();Log.e(TAG, "ondestoryView");}@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();Log.e(TAG, "ondestory");}@Overridepublic void onDetach() {// TODO Auto-generated method stubsuper.onDetach();Log.d(TAG, "onDetach------");}}</span>最后来看下效果吧,只有MessageFragment填充了数据:
横屏情况下:
--------------本文系原创,转载请注明作者yanzi1225627
版权声明:本文为博主原创文章,未经博主允许不得转载。
更多相关文章
- Android中的布局和控件
- 【Android(安卓)开发】:Android布局中的几种常用属性
- Android(安卓)唯一识别码
- Android桌面应用
- Android(安卓)Camera数据流分析全程记录(非overlay方式)
- [置顶] 对Android(安卓)MVVM的理解
- Android(安卓)2.3.3 NFC分析
- Android(安卓)Notification 用法的4种形式
- Android创建桌面快捷方式