精华内容
下载资源
问答
  • android 仿QQ界面

    千次阅读 2015-07-19 15:48:00
    Android应用经典主界面框架之一:仿QQ (使用Fragment, 附源码) 分类: Android开发 2014-06-14 23:43 17633人阅读 评论(98) 收藏 举报 android应用框架Fragment 目录(?)[+] 一底部控制栏二顶部...
    Android应用经典主界面框架之一:仿QQ (使用Fragment, 附源码)
    
    
    分类: Android开发 17633人阅读 评论(98) 收藏 举报

    最近反复研究日常经典必用的几个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就作了这样一件事:

    1. <SPAN style="FONT-FAMILY: Comic Sans MS; FONT-SIZE: 18px">package org.yanzi.ui;  
    2.   
    3. import org.yanzi.constant.Constant;  
    4.   
    5. import android.content.Context;  
    6. import android.graphics.Color;  
    7. import android.util.AttributeSet;  
    8. import android.view.LayoutInflater;  
    9. import android.view.MotionEvent;  
    10. import android.view.View;  
    11. import android.view.ViewGroup;  
    12. import android.widget.ImageView;  
    13. import android.widget.LinearLayout;  
    14. import android.widget.TextView;  
    15.   
    16. import com.example.fragmentproject.R;  
    17.   
    18.   
    19. public class ImageText extends LinearLayout{  
    20.     private Context mContext = null;  
    21.     private ImageView mImageView = null;  
    22.     private TextView mTextView = null;  
    23.     private final static int DEFAULT_IMAGE_WIDTH = 64;  
    24.     private final static int DEFAULT_IMAGE_HEIGHT = 64;  
    25.     private int CHECKED_COLOR = Color.rgb(29118199); //选中蓝色   
    26.     private int UNCHECKED_COLOR = Color.GRAY;   //自然灰色   
    27.     public ImageText(Context context) {  
    28.         super(context);  
    29.         // TODO Auto-generated constructor stub   
    30.         mContext = context;  
    31.     }  
    32.   
    33.     public ImageText(Context context, AttributeSet attrs) {  
    34.         super(context, attrs);  
    35.         // TODO Auto-generated constructor stub   
    36.         mContext = context;  
    37.         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
    38.         View parentView = inflater.inflate(R.layout.image_text_layout, thistrue);  
    39.         mImageView = (ImageView)findViewById(R.id.image_iamge_text);  
    40.         mTextView = (TextView)findViewById(R.id.text_iamge_text);  
    41.     }  
    42.     public void setImage(int id){  
    43.         if(mImageView != null){  
    44.             mImageView.setImageResource(id);  
    45.             setImageSize(DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT);  
    46.         }  
    47.     }  
    48.   
    49.     public void setText(String s){  
    50.         if(mTextView != null){  
    51.             mTextView.setText(s);  
    52.             mTextView.setTextColor(UNCHECKED_COLOR);  
    53.         }  
    54.     }  
    55.   
    56.     @Override  
    57.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
    58.         // TODO Auto-generated method stub   
    59.         return true;  
    60.     }  
    61.     private void setImageSize(int w, int h){  
    62.         if(mImageView != null){  
    63.             ViewGroup.LayoutParams params = mImageView.getLayoutParams();  
    64.             params.width = w;  
    65.             params.height = h;  
    66.             mImageView.setLayoutParams(params);  
    67.         }  
    68.     }  
    69.     public void setChecked(int itemID){  
    70.         if(mTextView != null){  
    71.             mTextView.setTextColor(CHECKED_COLOR);  
    72.         }  
    73.         int checkDrawableId = -1;  
    74.         switch (itemID){  
    75.         case Constant.BTN_FLAG_MESSAGE:  
    76.             checkDrawableId = R.drawable.message_selected;  
    77.             break;  
    78.         case Constant.BTN_FLAG_CONTACTS:  
    79.             checkDrawableId = R.drawable.contacts_selected;  
    80.             break;  
    81.         case Constant.BTN_FLAG_NEWS:  
    82.             checkDrawableId = R.drawable.news_selected;  
    83.             break;  
    84.         case Constant.BTN_FLAG_SETTING:  
    85.             checkDrawableId = R.drawable.setting_selected;  
    86.             break;  
    87.         default:break;  
    88.         }  
    89.         if(mImageView != null){  
    90.             mImageView.setImageResource(checkDrawableId);  
    91.         }  
    92.     }  
    93.   
    94.   
    95.       
    96.   
    97.   
    98. }  
    99. </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 stub
    		mContext = context;
    	}
    
    	public ImageText(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		// TODO Auto-generated constructor stub
    		mContext = 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);
    		}
    	}
    
    	@Override
    	public boolean onInterceptTouchEvent(MotionEvent ev) {
    		// TODO Auto-generated method stub
    		return 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>

    对应的布局:

    1. <SPAN style="FONT-FAMILY: Comic Sans MS; FONT-SIZE: 18px"><?xml version="1.0" encoding="utf-8"?>  
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     android:layout_width="match_parent"  
    4.     android:layout_height="match_parent"  
    5.     android:orientation="vertical" >  
    6.   
    7.     <ImageView  
    8.         android:id="@+id/image_iamge_text"  
    9.         android:layout_width="wrap_content"  
    10.         android:layout_height="wrap_content"  
    11.         android:layout_gravity="center_horizontal" />  
    12.   
    13.     <TextView  
    14.         android:id="@+id/text_iamge_text"  
    15.         android:layout_width="wrap_content"  
    16.         android:layout_height="wrap_content"  
    17.         android:layout_gravity="center_horizontal" />  
    18.   
    19. </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,这里面放的都是常量:

    1. <SPAN style="FONT-FAMILY: Comic Sans MS; FONT-SIZE: 18px">package org.yanzi.constant;  
    2.   
    3. public class Constant {  
    4.     //Btn的标识   
    5.     public static final int BTN_FLAG_MESSAGE = 0x01;  
    6.     public static final int BTN_FLAG_CONTACTS = 0x01 << 1;  
    7.     public static final int BTN_FLAG_NEWS = 0x01 << 2;  
    8.     public static final int BTN_FLAG_SETTING = 0x01 << 3;  
    9.       
    10.     //Fragment的标识   
    11.     public static final String FRAGMENT_FLAG_MESSAGE = "消息";   
    12.     public static final String FRAGMENT_FLAG_CONTACTS = "联系人";   
    13.     public static final String FRAGMENT_FLAG_NEWS = "新闻";   
    14.     public static final String FRAGMENT_FLAG_SETTING = "设置";   
    15.     public static final String FRAGMENT_FLAG_SIMPLE = "simple";   
    16.       
    17.       
    18. }  
    19. </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

    1. <SPAN style="FONT-FAMILY: Comic Sans MS; FONT-SIZE: 18px"><?xml version="1.0" encoding="utf-8"?>  
    2. <org.yanzi.ui.BottomControlPanel xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     android:layout_width="match_parent"  
    4.     android:layout_height="60dp"  
    5.     android:layout_alignParentBottom="true"  
    6.     android:gravity="center_vertical"  
    7.     android:paddingLeft="20dp"  
    8.     android:paddingRight="20dp" >  
    9.   
    10.     <org.yanzi.ui.ImageText  
    11.         android:id="@+id/btn_message"  
    12.         android:layout_width="wrap_content"  
    13.         android:layout_height="wrap_content"  
    14.         android:layout_alignParentLeft="true" />  
    15.   
    16.     <org.yanzi.ui.ImageText  
    17.         android:id="@+id/btn_contacts"  
    18.         android:layout_width="wrap_content"  
    19.         android:layout_height="wrap_content"  
    20.         android:layout_toRightOf="@id/btn_message" />  
    21.   
    22.     <org.yanzi.ui.ImageText  
    23.         android:id="@+id/btn_news"  
    24.         android:layout_width="wrap_content"  
    25.         android:layout_height="wrap_content"  
    26.         android:layout_toRightOf="@id/btn_contacts" />  
    27.   
    28.     <org.yanzi.ui.ImageText  
    29.         android:id="@+id/btn_setting"  
    30.         android:layout_width="wrap_content"  
    31.         android:layout_height="wrap_content"  
    32.         android:layout_alignParentRight="true" />  
    33.   
    34. </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

    1. <SPAN style="FONT-FAMILY: Comic Sans MS; FONT-SIZE: 18px">package org.yanzi.ui;  
    2.   
    3. import java.util.ArrayList;  
    4. import java.util.List;  
    5.   
    6. import org.yanzi.constant.Constant;  
    7.   
    8. import android.content.Context;  
    9. import android.graphics.Color;  
    10. import android.util.AttributeSet;  
    11. import android.util.Log;  
    12. import android.view.View;  
    13. import android.widget.RelativeLayout;  
    14.   
    15. import com.example.fragmentproject.R;  
    16.   
    17. public class BottomControlPanel extends RelativeLayout implements View.OnClickListener {  
    18.     private Context mContext;  
    19.     private ImageText mMsgBtn = null;  
    20.     private ImageText mContactsBtn = null;  
    21.     private ImageText mNewsBtn = null;  
    22.     private ImageText mSettingBtn = null;  
    23.     private int DEFALUT_BACKGROUND_COLOR = Color.rgb(243243243); //Color.rgb(192, 192, 192)   
    24.     private BottomPanelCallback mBottomCallback = null;  
    25.     private List<ImageText> viewList = new ArrayList<ImageText>();  
    26.   
    27.     public interface BottomPanelCallback{  
    28.         public void onBottomPanelClick(int itemId);  
    29.     }  
    30.     public BottomControlPanel(Context context, AttributeSet attrs) {  
    31.         super(context, attrs);  
    32.         // TODO Auto-generated constructor stub   
    33.     }  
    34.     @Override  
    35.     protected void onFinishInflate() {  
    36.         // TODO Auto-generated method stub   
    37.         mMsgBtn = (ImageText)findViewById(R.id.btn_message);  
    38.         mContactsBtn = (ImageText)findViewById(R.id.btn_contacts);  
    39.         mNewsBtn = (ImageText)findViewById(R.id.btn_news);  
    40.         mSettingBtn = (ImageText)findViewById(R.id.btn_setting);  
    41.         setBackgroundColor(DEFALUT_BACKGROUND_COLOR);  
    42.         viewList.add(mMsgBtn);  
    43.         viewList.add(mContactsBtn);  
    44.         viewList.add(mNewsBtn);  
    45.         viewList.add(mSettingBtn);  
    46.   
    47.     }  
    48.     public void initBottomPanel(){  
    49.         if(mMsgBtn != null){  
    50.             mMsgBtn.setImage(R.drawable.message_unselected);  
    51.             mMsgBtn.setText("消息");  
    52.         }  
    53.         if(mContactsBtn != null){  
    54.             mContactsBtn.setImage(R.drawable.contacts_unselected);  
    55.             mContactsBtn.setText("联系人");  
    56.         }  
    57.         if(mNewsBtn != null){  
    58.             mNewsBtn.setImage(R.drawable.news_unselected);  
    59.             mNewsBtn.setText("新闻");  
    60.         }  
    61.         if(mSettingBtn != null){  
    62.             mSettingBtn.setImage(R.drawable.setting_unselected);  
    63.             mSettingBtn.setText("设置");  
    64.         }  
    65.         setBtnListener();  
    66.   
    67.     }  
    68.     private void setBtnListener(){  
    69.         int num = this.getChildCount();  
    70.         for(int i = 0; i < num; i++){  
    71.             View v = getChildAt(i);  
    72.             if(v != null){  
    73.                 v.setOnClickListener(this);  
    74.             }  
    75.         }  
    76.     }  
    77.     public void setBottomCallback(BottomPanelCallback bottomCallback){  
    78.         mBottomCallback = bottomCallback;  
    79.     }  
    80.     @Override  
    81.     public void onClick(View v) {  
    82.         // TODO Auto-generated method stub   
    83.         initBottomPanel();  
    84.         int index = -1;  
    85.         switch(v.getId()){  
    86.         case R.id.btn_message:  
    87.             index = Constant.BTN_FLAG_MESSAGE;  
    88.             mMsgBtn.setChecked(Constant.BTN_FLAG_MESSAGE);  
    89.             break;  
    90.         case R.id.btn_contacts:  
    91.             index = Constant.BTN_FLAG_CONTACTS;  
    92.             mContactsBtn.setChecked(Constant.BTN_FLAG_CONTACTS);  
    93.             break;  
    94.         case R.id.btn_news:  
    95.             index = Constant.BTN_FLAG_NEWS;  
    96.             mNewsBtn.setChecked(Constant.BTN_FLAG_NEWS);  
    97.             break;  
    98.         case R.id.btn_setting:  
    99.             index = Constant.BTN_FLAG_SETTING;  
    100.             mSettingBtn.setChecked(Constant.BTN_FLAG_SETTING);  
    101.             break;  
    102.         default:break;  
    103.         }  
    104.         if(mBottomCallback != null){  
    105.             mBottomCallback.onBottomPanelClick(index);  
    106.         }  
    107.     }  
    108.     public void defaultBtnChecked(){  
    109.         if(mMsgBtn != null){  
    110.             mMsgBtn.setChecked(Constant.BTN_FLAG_MESSAGE);  
    111.         }  
    112.     }  
    113.     @Override  
    114.     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
    115.         // TODO Auto-generated method stub   
    116.         super.onLayout(changed, left, top, right, bottom);  
    117.         layoutItems(left, top, right, bottom);  
    118.     }  
    119.     /**最左边和最右边的view由母布局的padding进行控制位置。这里需对第2、3个view的位置重新设置 
    120.      * @param left 
    121.      * @param top 
    122.      * @param right 
    123.      * @param bottom 
    124.      */  
    125.     private void layoutItems(int left, int top, int right, int bottom){  
    126.         int n = getChildCount();  
    127.         if(n == 0){  
    128.             return;  
    129.         }  
    130.         int paddingLeft = getPaddingLeft();  
    131.         int paddingRight = getPaddingRight();  
    132.         Log.i("yanguoqi""paddingLeft = " + paddingLeft + " paddingRight = " + paddingRight);  
    133.         int width = right - left;  
    134.         int height = bottom - top;  
    135.         Log.i("yanguoqi""width = " + width + " height = " + height);  
    136.         int allViewWidth = 0;  
    137.         for(int i = 0; i< n; i++){  
    138.             View v = getChildAt(i);  
    139.             Log.i("yanguoqi""v.getWidth() = " + v.getWidth());  
    140.             allViewWidth += v.getWidth();  
    141.         }  
    142.         int blankWidth = (width - allViewWidth - paddingLeft - paddingRight) / (n - 1);  
    143.         Log.i("yanguoqi""blankV = " + blankWidth );  
    144.   
    145.         LayoutParams params1 = (LayoutParams) viewList.get(1).getLayoutParams();  
    146.         params1.leftMargin = blankWidth;  
    147.         viewList.get(1).setLayoutParams(params1);  
    148.   
    149.         LayoutParams params2 = (LayoutParams) viewList.get(2).getLayoutParams();  
    150.         params2.leftMargin = blankWidth;  
    151.         viewList.get(2).setLayoutParams(params2);  
    152.     }  
    153.   
    154.   
    155.   
    156. }  
    157. </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
    	}
    	@Override
    	protected void onFinishInflate() {
    		// TODO Auto-generated method stub
    		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);
    
    	}
    	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;
    	}
    	@Override
    	public void onClick(View v) {
    		// TODO Auto-generated method stub
    		initBottomPanel();
    		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);
    		}
    	}
    	@Override
    	protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    		// TODO Auto-generated method stub
    		super.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

    1. <SPAN style="FONT-FAMILY: Comic Sans MS; FONT-SIZE: 18px">package org.yanzi.ui;  
    2.   
    3. import org.yanzi.constant.Constant;  
    4.   
    5. import com.example.fragmentproject.R;  
    6.   
    7. import android.content.Context;  
    8. import android.graphics.Color;  
    9. import android.util.AttributeSet;  
    10. import android.widget.RelativeLayout;  
    11. import android.widget.TextView;  
    12.   
    13. public class HeadControlPanel extends RelativeLayout {  
    14.   
    15.     private Context mContext;  
    16.     private TextView mMidleTitle;  
    17.     private TextView mRightTitle;  
    18.     private static final float middle_title_size = 20f;   
    19.     private static final float right_title_size = 17f;   
    20.     private static final int default_background_color = Color.rgb(23, 124, 202);  
    21.       
    22.     public HeadControlPanel(Context context, AttributeSet attrs) {  
    23.         super(context, attrs);  
    24.         // TODO Auto-generated constructor stub  
    25.     }  
    26.   
    27.     @Override  
    28.     protected void onFinishInflate() {  
    29.         // TODO Auto-generated method stub  
    30.         mMidleTitle = (TextView)findViewById(R.id.midle_title);  
    31.         mRightTitle = (TextView)findViewById(R.id.right_title);  
    32.         setBackgroundColor(default_background_color);  
    33.     }  
    34.     public void initHeadPanel(){  
    35.           
    36.         if(mMidleTitle != null){  
    37.             setMiddleTitle(Constant.FRAGMENT_FLAG_MESSAGE);  
    38.         }  
    39.     }  
    40.     public void setMiddleTitle(String s){  
    41.         mMidleTitle.setText(s);  
    42.         mMidleTitle.setTextSize(middle_title_size);  
    43.     }  
    44.       
    45.   
    46. }  
    47. </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
    	}
    
    	@Override
    	protected void onFinishInflate() {
    		// TODO Auto-generated method stub
    		mMidleTitle = (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

    1. <SPAN style="FONT-FAMILY: Comic Sans MS; FONT-SIZE: 18px"><?xml version="1.0" encoding="utf-8"?>  
    2. <org.yanzi.ui.HeadControlPanel xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     android:layout_width="match_parent"  
    4.     android:layout_height="50dp"  
    5.     android:layout_alignParentTop="true">  
    6.     <TextView   
    7.         android:id="@+id/midle_title"  
    8.         android:layout_width="wrap_content"  
    9.         android:layout_height="wrap_content"  
    10.         android:layout_centerInParent="true"  
    11.         android:textColor="@android:color/white"/>  
    12.     <TextView   
    13.          android:id="@+id/right_title"  
    14.         android:layout_width="wrap_content"  
    15.         android:layout_height="wrap_content"  
    16.         android:layout_alignParentRight="true"  
    17.         android:textColor="@android:color/white"/>  
    18.           
    19. </org.yanzi.ui.HeadControlPanel>  
    20. </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:

    1. <SPAN style="FONT-FAMILY: Comic Sans MS; FONT-SIZE: 18px"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     xmlns:tools="http://schemas.android.com/tools"  
    3.     android:id="@+id/root_layout"  
    4.     android:layout_width="match_parent"  
    5.     android:layout_height="match_parent"  
    6.     tools:context="org.yanzi.fragmentproject.MainActivity" >  
    7.   
    8.     <include  
    9.         android:id="@+id/bottom_layout"  
    10.         layout="@layout/bottom_panel_layout" />  
    11.   
    12.     <View  
    13.         android:layout_width="match_parent"  
    14.         android:layout_height="1dip"  
    15.         android:layout_above="@id/bottom_layout"  
    16.         android:background="#FFE7E7E7" />  
    17.   
    18.     <include  
    19.         android:id="@+id/head_layout"  
    20.         layout="@layout/head_panel_layout" />  
    21.   
    22.     <View  
    23.         android:layout_width="match_parent"  
    24.         android:layout_height="1dip"  
    25.         android:layout_below="@id/head_layout"  
    26.         android:background="#FFE7E7E7" />  
    27.     <FrameLayout  
    28.         android:id="@+id/fragment_content"  
    29.         android:layout_width="match_parent"  
    30.         android:layout_height="wrap_content"  
    31.         android:layout_below="@id/head_layout"  
    32.         android:layout_above="@id/bottom_layout" >  
    33.     </FrameLayout>  
    34.   
    35. </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代码:

    1. <SPAN style="FONT-FAMILY: Comic Sans MS; FONT-SIZE: 18px">package org.yanzi.activity;  
    2.   
    3. import org.yanzi.constant.Constant;  
    4. import org.yanzi.fragment.BaseFragment;  
    5. import org.yanzi.fragment.ContactsFragment;  
    6. import org.yanzi.fragment.MessageFragment;  
    7. import org.yanzi.fragment.NewsFragment;  
    8. import org.yanzi.fragment.SettingFragment;  
    9. import org.yanzi.ui.BottomControlPanel;  
    10. import org.yanzi.ui.BottomControlPanel.BottomPanelCallback;  
    11. import org.yanzi.ui.HeadControlPanel;  
    12.   
    13. import android.app.Activity;  
    14. import android.app.Fragment;  
    15. import android.app.FragmentManager;  
    16. import android.app.FragmentTransaction;  
    17. import android.os.Bundle;  
    18. import android.text.TextUtils;  
    19. import android.util.Log;  
    20. import android.view.Menu;  
    21. import android.widget.Toast;  
    22.   
    23. import com.example.fragmentproject.R;  
    24.   
    25. public class MainActivity extends Activity implements BottomPanelCallback {  
    26.     BottomControlPanel bottomPanel = null;  
    27.     HeadControlPanel headPanel = null;  
    28.       
    29.     private FragmentManager fragmentManager = null;  
    30.     private FragmentTransaction fragmentTransaction = null;  
    31.       
    32. /*  private MessageFragment messageFragment; 
    33.     private ContactsFragment contactsFragment; 
    34.     private NewsFragment newsFragment; 
    35.     private SettingFragment settingFragment;*/  
    36.       
    37.     public static String currFragTag = "";  
    38.     @Override  
    39.     protected void onCreate(Bundle savedInstanceState) {  
    40.         super.onCreate(savedInstanceState);  
    41.         setContentView(R.layout.activity_main);  
    42.         initUI();  
    43.         fragmentManager = getFragmentManager();  
    44.         setDefaultFirstFragment(Constant.FRAGMENT_FLAG_MESSAGE);  
    45.     }  
    46.   
    47.     @Override  
    48.     public boolean onCreateOptionsMenu(Menu menu) {  
    49.         // Inflate the menu; this adds items to the action bar if it is present.   
    50.         getMenuInflater().inflate(R.menu.main, menu);  
    51.         return true;  
    52.     }  
    53.     private void initUI(){  
    54.         bottomPanel = (BottomControlPanel)findViewById(R.id.bottom_layout);  
    55.         if(bottomPanel != null){  
    56.             bottomPanel.initBottomPanel();  
    57.             bottomPanel.setBottomCallback(this);  
    58.         }  
    59.         headPanel = (HeadControlPanel)findViewById(R.id.head_layout);  
    60.         if(headPanel != null){  
    61.             headPanel.initHeadPanel();  
    62.         }  
    63.     }  
    64.   
    65.     /* 处理BottomControlPanel的回调 
    66.      * @see org.yanzi.ui.BottomControlPanel.BottomPanelCallback#onBottomPanelClick(int) 
    67.      */  
    68.     @Override  
    69.     public void onBottomPanelClick(int itemId) {  
    70.         // TODO Auto-generated method stub   
    71.         String tag = "";  
    72.         if((itemId & Constant.BTN_FLAG_MESSAGE) != 0){  
    73.             tag = Constant.FRAGMENT_FLAG_MESSAGE;  
    74.         }else if((itemId & Constant.BTN_FLAG_CONTACTS) != 0){  
    75.             tag = Constant.FRAGMENT_FLAG_CONTACTS;  
    76.         }else if((itemId & Constant.BTN_FLAG_NEWS) != 0){  
    77.             tag = Constant.FRAGMENT_FLAG_NEWS;  
    78.         }else if((itemId & Constant.BTN_FLAG_SETTING) != 0){  
    79.             tag = Constant.FRAGMENT_FLAG_SETTING;  
    80.         }  
    81.         setTabSelection(tag); //切换Fragment   
    82.         headPanel.setMiddleTitle(tag);//切换标题    
    83.     }  
    84.       
    85.     private void setDefaultFirstFragment(String tag){  
    86.         Log.i("yan""setDefaultFirstFragment enter... currFragTag = " + currFragTag);  
    87.         setTabSelection(tag);  
    88.         bottomPanel.defaultBtnChecked();  
    89.         Log.i("yan""setDefaultFirstFragment exit...");  
    90.     }  
    91.       
    92.     private void commitTransactions(String tag){  
    93.         if (fragmentTransaction != null && !fragmentTransaction.isEmpty()) {  
    94.             fragmentTransaction.commit();  
    95.             currFragTag = tag;  
    96.             fragmentTransaction = null;  
    97.         }  
    98.     }  
    99.       
    100.     private FragmentTransaction ensureTransaction( ){  
    101.         if(fragmentTransaction == null){  
    102.             fragmentTransaction = fragmentManager.beginTransaction();  
    103.             fragmentTransaction  
    104.             .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);  
    105.               
    106.         }  
    107.         return fragmentTransaction;  
    108.           
    109.     }  
    110.       
    111.     private void attachFragment(int layout, Fragment f, String tag){  
    112.         if(f != null){  
    113.             if(f.isDetached()){  
    114.                 ensureTransaction();  
    115.                 fragmentTransaction.attach(f);  
    116.                   
    117.             }else if(!f.isAdded()){  
    118.                 ensureTransaction();  
    119.                 fragmentTransaction.add(layout, f, tag);  
    120.             }  
    121.         }  
    122.     }  
    123.       
    124.     private Fragment getFragment(String tag){  
    125.           
    126.         Fragment f = fragmentManager.findFragmentByTag(tag);  
    127.           
    128.         if(f == null){  
    129.             Toast.makeText(getApplicationContext(), "fragment = null tag = " + tag, Toast.LENGTH_SHORT).show();  
    130.             f = BaseFragment.newInstance(getApplicationContext(), tag);  
    131.         }  
    132.         return f;  
    133.           
    134.     }  
    135.     private void detachFragment(Fragment f){  
    136.           
    137.         if(f != null && !f.isDetached()){  
    138.             ensureTransaction();  
    139.             fragmentTransaction.detach(f);  
    140.         }  
    141.     }  
    142.     /**切换fragment  
    143.      * @param tag 
    144.      */  
    145.     private  void switchFragment(String tag){  
    146.         if(TextUtils.equals(tag, currFragTag)){  
    147.             return;  
    148.         }  
    149.         //把上一个fragment detach掉    
    150.         if(currFragTag != null && !currFragTag.equals("")){  
    151.             detachFragment(getFragment(currFragTag));  
    152.         }  
    153.         attachFragment(R.id.fragment_content, getFragment(tag), tag);  
    154.         commitTransactions( tag);  
    155.     }   
    156.       
    157.     /**设置选中的Tag 
    158.      * @param tag 
    159.      */  
    160.     public  void setTabSelection(String tag) {  
    161.         // 开启一个Fragment事务   
    162.         fragmentTransaction = fragmentManager.beginTransaction();  
    163. /*       if(TextUtils.equals(tag, Constant.FRAGMENT_FLAG_MESSAGE)){ 
    164.            if (messageFragment == null) { 
    165.                 messageFragment = new MessageFragment(); 
    166.             }  
    167.              
    168.         }else if(TextUtils.equals(tag, Constant.FRAGMENT_FLAG_CONTACTS)){ 
    169.             if (contactsFragment == null) { 
    170.                 contactsFragment = new ContactsFragment(); 
    171.             }  
    172.              
    173.         }else if(TextUtils.equals(tag, Constant.FRAGMENT_FLAG_NEWS)){ 
    174.             if (newsFragment == null) { 
    175.                 newsFragment = new NewsFragment(); 
    176.             } 
    177.              
    178.         }else if(TextUtils.equals(tag,Constant.FRAGMENT_FLAG_SETTING)){ 
    179.             if (settingFragment == null) { 
    180.                 settingFragment = new SettingFragment(); 
    181.             } 
    182.         }else if(TextUtils.equals(tag, Constant.FRAGMENT_FLAG_SIMPLE)){ 
    183.             if (simpleFragment == null) { 
    184.                 simpleFragment = new SimpleFragment(); 
    185.             }  
    186.              
    187.         }*/  
    188.          switchFragment(tag);  
    189.            
    190.     }  
    191.   
    192.     @Override  
    193.     protected void onStop() {  
    194.         // TODO Auto-generated method stub   
    195.         super.onStop();  
    196.         currFragTag = "";  
    197.     }  
    198.   
    199.     @Override  
    200.     protected void onSaveInstanceState(Bundle outState) {  
    201.         // TODO Auto-generated method stub   
    202.     }  
    203.   
    204. }  
    205. </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 = "";
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		initUI();
    		fragmentManager = getFragmentManager();
    		setDefaultFirstFragment(Constant.FRAGMENT_FLAG_MESSAGE);
    	}
    
    	@Override
    	public 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)
    	 */
    	@Override
    	public void onBottomPanelClick(int itemId) {
    		// TODO Auto-generated method stub
    		String 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); //切换Fragment
    		headPanel.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);
    		 
    	}
    
    	@Override
    	protected void onStop() {
    		// TODO Auto-generated method stub
    		super.onStop();
    		currFragTag = "";
    	}
    
    	@Override
    	protected 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

    1. <SPAN style="FONT-FAMILY: Comic Sans MS; FONT-SIZE: 18px">package org.yanzi.bean;  
    2.   
    3. public class MessageBean {  
    4.     private int PhotoDrawableId;  
    5.     private String MessageName;  
    6.     private String MessageContent;  
    7.     private String MessageTime;  
    8.       
    9.     public MessageBean(){  
    10.           
    11.     }  
    12.       
    13.     public MessageBean(int photoDrawableId, String messageName,  
    14.             String messageContent, String messageTime) {  
    15.         super();  
    16.         PhotoDrawableId = photoDrawableId;  
    17.         MessageName = messageName;  
    18.         MessageContent = messageContent;  
    19.         MessageTime = messageTime;  
    20.     }  
    21.   
    22.     public int getPhotoDrawableId() {  
    23.         return PhotoDrawableId;  
    24.     }  
    25.     public void setPhotoDrawableId(int mPhotoDrawableId) {  
    26.         this.PhotoDrawableId = mPhotoDrawableId;  
    27.     }  
    28.     public String getMessageName() {  
    29.         return MessageName;  
    30.     }  
    31.     public void setMessageName(String messageName) {  
    32.         MessageName = messageName;  
    33.     }  
    34.     public String getMessageContent() {  
    35.         return MessageContent;  
    36.     }  
    37.     public void setMessageContent(String messageContent) {  
    38.         MessageContent = messageContent;  
    39.     }  
    40.     public String getMessageTime() {  
    41.         return MessageTime;  
    42.     }  
    43.     public void setMessageTime(String messageTime) {  
    44.         MessageTime = messageTime;  
    45.     }  
    46.     @Override  
    47.     public String toString() {  
    48.         return "MessageBean [mPhotoDrawableId=" + PhotoDrawableId  
    49.                 + ", MessageName=" + MessageName + ", MessageContent="  
    50.                 + MessageContent + ", MessageTime=" + MessageTime + "]";  
    51.     }  
    52.       
    53.       
    54.       
    55. }  
    56. </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;
    	}
    	@Override
    	public String toString() {
    		return "MessageBean [mPhotoDrawableId=" + PhotoDrawableId
    				+ ", MessageName=" + MessageName + ", MessageContent="
    				+ MessageContent + ", MessageTime=" + MessageTime + "]";
    	}
    	
    	
    	
    }
    </span>
    然后就是MessageFragment的ListView里的适配器,MessageAdapter.java

    1. <SPAN style="FONT-FAMILY: Comic Sans MS; FONT-SIZE: 18px">package org.yanzi.fragment.adapter;  
    2.   
    3. import java.util.List;  
    4.   
    5. import org.yanzi.bean.MessageBean;  
    6.   
    7. import com.example.fragmentproject.R;  
    8.   
    9. import android.content.Context;  
    10. import android.view.LayoutInflater;  
    11. import android.view.View;  
    12. import android.view.ViewGroup;  
    13. import android.widget.BaseAdapter;  
    14. import android.widget.ImageView;  
    15. import android.widget.TextView;  
    16.   
    17. public class MessageAdapter extends BaseAdapter {  
    18.     private List<MessageBean> mListMsgBean = null;  
    19.     private Context mContext;  
    20.     private LayoutInflater mInflater;  
    21.     public MessageAdapter(List<MessageBean> listMsgBean, Context context){  
    22.         mListMsgBean = listMsgBean;  
    23.         mContext = context;  
    24.         mInflater = LayoutInflater.from(mContext);  
    25.     }  
    26.     @Override  
    27.     public int getCount() {  
    28.         // TODO Auto-generated method stub   
    29.         return mListMsgBean.size();  
    30.     }  
    31.   
    32.     @Override  
    33.     public Object getItem(int position) {  
    34.         // TODO Auto-generated method stub   
    35.         return mListMsgBean.get(position);  
    36.     }  
    37.   
    38.     @Override  
    39.     public long getItemId(int position) {  
    40.         // TODO Auto-generated method stub   
    41.         return position;  
    42.     }  
    43.   
    44.     @Override  
    45.     public View getView(int position, View convertView, ViewGroup parent) {  
    46.         // TODO Auto-generated method stub   
    47.         View v = mInflater.inflate(R.layout.message_item_layout, null);  
    48.           
    49.         ImageView imageView = (ImageView) v.findViewById(R.id.img_msg_item);  
    50.         imageView.setImageResource(mListMsgBean.get(position).getPhotoDrawableId());  
    51.           
    52.         TextView nameMsg = (TextView)v.findViewById(R.id.name_msg_item);  
    53.         nameMsg.setText(mListMsgBean.get(position).getMessageName());  
    54.   
    55.         TextView contentMsg = (TextView)v.findViewById(R.id.content_msg_item);  
    56.         contentMsg.setText(mListMsgBean.get(position).getMessageContent());  
    57.           
    58.         TextView timeMsg = (TextView)v.findViewById(R.id.time_msg_item);  
    59.         timeMsg.setText(mListMsgBean.get(position).getMessageTime());  
    60.   
    61.         return v;  
    62.     }  
    63.   
    64. }  
    65. </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);
    	}
    	@Override
    	public int getCount() {
    		// TODO Auto-generated method stub
    		return mListMsgBean.size();
    	}
    
    	@Override
    	public Object getItem(int position) {
    		// TODO Auto-generated method stub
    		return mListMsgBean.get(position);
    	}
    
    	@Override
    	public long getItemId(int position) {
    		// TODO Auto-generated method stub
    		return position;
    	}
    
    	@Override
    	public View getView(int position, View convertView, ViewGroup parent) {
    		// TODO Auto-generated method stub
    		View 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代码:

    1. <SPAN style="FONT-FAMILY: Comic Sans MS; FONT-SIZE: 18px">package org.yanzi.fragment;  
    2.   
    3. import java.util.ArrayList;  
    4. import java.util.List;  
    5.   
    6. import org.yanzi.activity.MainActivity;  
    7. import org.yanzi.bean.MessageBean;  
    8. import org.yanzi.constant.Constant;  
    9. import org.yanzi.fragment.adapter.MessageAdapter;  
    10.   
    11. import android.app.Activity;  
    12. import android.os.Bundle;  
    13. import android.util.Log;  
    14. import android.view.LayoutInflater;  
    15. import android.view.View;  
    16. import android.view.ViewGroup;  
    17. import android.widget.AdapterView;  
    18. import android.widget.Toast;  
    19. import android.widget.AdapterView.OnItemClickListener;  
    20. import android.widget.ListView;  
    21.   
    22. import com.example.fragmentproject.R;  
    23.   
    24. public class MessageFragment extends BaseFragment {  
    25.   
    26.     private static final String TAG = "MessageFragment";  
    27.     private MainActivity mMainActivity ;  
    28.     private ListView mListView;  
    29.     private MessageAdapter mMsgAdapter;  
    30.     private List<MessageBean> mMsgBean = new ArrayList<MessageBean>();  
    31.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
    32.             Bundle savedInstanceState) {  
    33.         View messageLayout = inflater.inflate(R.layout.message_layout,  
    34.                 container, false);  
    35.         Log.d(TAG, "onCreateView---->");  
    36.         mMainActivity = (MainActivity) getActivity();  
    37.         mFragmentManager = getActivity().getFragmentManager();  
    38.         mListView = (ListView)messageLayout.findViewById(R.id.listview_message);  
    39.         mMsgAdapter = new MessageAdapter(mMsgBean, mMainActivity);  
    40.         mListView.setAdapter(mMsgAdapter);  
    41.         mListView.setOnItemClickListener(new OnItemClickListener() {  
    42.   
    43.             @Override  
    44.             public void onItemClick(AdapterView<?> parent, View view,  
    45.                     int position, long id) {  
    46.                 // TODO Auto-generated method stub   
    47.                 Toast.makeText(mMainActivity, mMsgBean.get(position).toString(),  
    48.                         Toast.LENGTH_SHORT).show();  
    49.             }  
    50.   
    51.         });  
    52.         return messageLayout;  
    53.     }  
    54.   
    55.   
    56.     @Override  
    57.     public void onAttach(Activity activity) {  
    58.         // TODO Auto-generated method stub   
    59.         super.onAttach(activity);  
    60.         Log.e(TAG, "onAttach-----");  
    61.   
    62.     }  
    63.     @Override  
    64.     public void onCreate(Bundle savedInstanceState) {  
    65.         // TODO Auto-generated method stub   
    66.         super.onCreate(savedInstanceState);  
    67.         Log.e(TAG, "onCreate------");  
    68.         mMsgBean.add(new MessageBean(R.drawable.ic_photo_1, "张三""吃饭没?""昨天"));  
    69.         mMsgBean.add(new MessageBean(R.drawable.ic_photo_2, "李四""哈哈""昨天"));  
    70.         mMsgBean.add(new MessageBean(R.drawable.ic_photo_3, "小明""吃饭没?""昨天"));  
    71.         mMsgBean.add(new MessageBean(R.drawable.ic_photo_4, "王五""吃饭没?""昨天"));  
    72.         mMsgBean.add(new MessageBean(R.drawable.ic_photo_5, "Jack""吃饭没?""昨天"));  
    73.         mMsgBean.add(new MessageBean(R.drawable.ic_photo_6, "Jone""吃饭没?""昨天"));  
    74.         mMsgBean.add(new MessageBean(R.drawable.ic_photo_7, "Jone""吃饭没?""昨天"));  
    75.         mMsgBean.add(new MessageBean(R.drawable.ic_photo_8, "Jone""吃饭没?""昨天"));  
    76.         mMsgBean.add(new MessageBean(R.drawable.ic_photo_9, "Jone""吃饭没?""昨天"));  
    77.     }  
    78.   
    79.     @Override  
    80.     public void onActivityCreated(Bundle savedInstanceState) {  
    81.         // TODO Auto-generated method stub   
    82.         super.onActivityCreated(savedInstanceState);  
    83.         Log.e(TAG, "onActivityCreated-------");  
    84.     }  
    85.   
    86.     @Override  
    87.     public void onStart() {  
    88.         // TODO Auto-generated method stub   
    89.         super.onStart();  
    90.   
    91.         Log.e(TAG, "onStart----->");  
    92.     }  
    93.   
    94.     @Override  
    95.     public void onResume() {  
    96.         // TODO Auto-generated method stub   
    97.         super.onResume();  
    98.         Log.e(TAG, "onresume---->");  
    99.         MainActivity.currFragTag = Constant.FRAGMENT_FLAG_MESSAGE;  
    100.     }  
    101.   
    102.     @Override  
    103.     public void onPause() {  
    104.         // TODO Auto-generated method stub   
    105.         super.onPause();  
    106.         Log.e(TAG, "onpause");  
    107.     }  
    108.   
    109.     @Override  
    110.     public void onStop() {  
    111.         // TODO Auto-generated method stub   
    112.         super.onStop();  
    113.         Log.e(TAG, "onStop");  
    114.     }  
    115.   
    116.     @Override  
    117.     public void onDestroyView() {  
    118.         // TODO Auto-generated method stub   
    119.         super.onDestroyView();  
    120.         Log.e(TAG, "ondestoryView");  
    121.     }  
    122.     @Override  
    123.     public void onDestroy() {  
    124.         // TODO Auto-generated method stub   
    125.         super.onDestroy();  
    126.         Log.e(TAG, "ondestory");  
    127.     }  
    128.   
    129.     @Override  
    130.     public void onDetach() {  
    131.         // TODO Auto-generated method stub   
    132.         super.onDetach();  
    133.         Log.d(TAG, "onDetach------");  
    134.   
    135.     }  
    136.   
    137.   
    138.   
    139. }  
    140. </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() {
    
    			@Override
    			public void onItemClick(AdapterView<?> parent, View view,
    					int position, long id) {
    				// TODO Auto-generated method stub
    				Toast.makeText(mMainActivity, mMsgBean.get(position).toString(),
    						Toast.LENGTH_SHORT).show();
    			}
    
    		});
    		return messageLayout;
    	}
    
    
    	@Override
    	public void onAttach(Activity activity) {
    		// TODO Auto-generated method stub
    		super.onAttach(activity);
    		Log.e(TAG, "onAttach-----");
    
    	}
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		// TODO Auto-generated method stub
    		super.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", "吃饭没?", "昨天"));
    	}
    
    	@Override
    	public void onActivityCreated(Bundle savedInstanceState) {
    		// TODO Auto-generated method stub
    		super.onActivityCreated(savedInstanceState);
    		Log.e(TAG, "onActivityCreated-------");
    	}
    
    	@Override
    	public void onStart() {
    		// TODO Auto-generated method stub
    		super.onStart();
    
    		Log.e(TAG, "onStart----->");
    	}
    
    	@Override
    	public void onResume() {
    		// TODO Auto-generated method stub
    		super.onResume();
    		Log.e(TAG, "onresume---->");
    		MainActivity.currFragTag = Constant.FRAGMENT_FLAG_MESSAGE;
    	}
    
    	@Override
    	public void onPause() {
    		// TODO Auto-generated method stub
    		super.onPause();
    		Log.e(TAG, "onpause");
    	}
    
    	@Override
    	public void onStop() {
    		// TODO Auto-generated method stub
    		super.onStop();
    		Log.e(TAG, "onStop");
    	}
    
    	@Override
    	public void onDestroyView() {
    		// TODO Auto-generated method stub
    		super.onDestroyView();
    		Log.e(TAG, "ondestoryView");
    	}
    	@Override
    	public void onDestroy() {
    		// TODO Auto-generated method stub
    		super.onDestroy();
    		Log.e(TAG, "ondestory");
    	}
    
    	@Override
    	public void onDetach() {
    		// TODO Auto-generated method stub
    		super.onDetach();
    		Log.d(TAG, "onDetach------");
    
    	}
    
    
    
    }
    </span>
    最后来看下效果吧,只有MessageFragment填充了数据:

          


    横屏情况下:


    --------------本文系原创,转载请注明作者yanzi1225627


    版权声明:本文为博主原创文章,未经博主允许不得转载。

    展开全文
  • Android 仿QQ界面的实现

    万次阅读 2015-12-26 17:56:54
    本项目是一个高仿QQ最新版本的项目,界面超级华丽,使用了大量的自定义控件,项目里实现了部分功能,例如WIFI-FTP(把手机变成FTP服务端,可以在WEB端管理手机文件) 、字母列表索引、自定义相机等,其余大多是实现...

    废话不说  上图  适合新手学习
















    更多功能自行下载源码学习


    展开全文
  • 思路:1、添加支持包2、在显示界面的 xml 中布局占位3、自定义一个可以水平滚动的 View 布局控件(侧滑删除)4、调用自定义的侧滑控件,实现单个 RecyclerView 的 xml 布局5、写一个与自定义侧滑控件匹配的适配器6、...

        RecyclerView 是 ListView 的进阶,实现方法和 ListView 类似,但是多了一些动画效果,其他方面也有优化,有兴趣的自己去了解!


    思路:

    1、添加支持包

    2、在显示界面的 xml 中布局占位

    3、自定义一个可以水平滚动的 View 布局控件(侧滑删除)

    4、调用自定义的侧滑控件,实现单个 RecyclerView 的 xml 布局

    5、写一个与自定义侧滑控件匹配的适配器

    6、在 Activity 调用并插入数据,实现效果


    效果展示:



    侧滑出现一个或多个按钮,点击实现想要的效果


    实现详情:

    1、添加支持包

    在 build.gradle 下的 dependencies 中添加:

    compile 'com.android.support:recyclerview-v7:25.2.0'

    2、在显示界面的 xml 中布局占位

    这里是在 activity_main.xml 中

    android.support.v7.widget.RecyclerView
        android:layout_weight="1"
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="vertical"
        android:overScrollMode="never"/>

    这只是主要的布局,其他的布局可以根据自己需要自己定义


    3、自定义一个可以水平滚动的 View 布局控件(侧滑删除)

    这里,自定义 RecyclerItemView 类,继承 HorizontalScrollView 具体如下:

    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.HorizontalScrollView;
    import android.widget.LinearLayout;
    
    /**
     * Created by Administrator on 2018/3/16 0016.
     * 具有滑动效果的 Item    需要继承水平滚动视图
     */
    
    public class RecyclerItemView extends HorizontalScrollView{
    
        private LinearLayout slide;//滑动弹出的按钮容器
    
        private int slideWidth; // 滑动弹出这个控件的宽度
    
        private onSlidingButtonListener onSbl;//滑动按钮侦听器
    
        private Boolean isOpen = false;//判断是否有删除按钮被打开
    
        public RecyclerItemView(Context context) {
            this(context, null);
        }
    
        public RecyclerItemView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public RecyclerItemView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            this.setOverScrollMode(OVER_SCROLL_NEVER);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                slide = (LinearLayout) findViewById(R.id.slide);
        }
    
        //通过布局获取按钮宽度
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            super.onLayout(changed, l, t, r, b);
            if(changed){
                this.scrollTo(0,0);
                //获取水平滚动条可以滑动的范围,即右侧按钮的宽度
                slideWidth = slide.getWidth();
            }
        }
    
        //触摸事件
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            int action = ev.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                case MotionEvent.ACTION_MOVE:
                    onSbl.onDownOrMove(this);
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    changeScrollx();
                    return true;
                default:
                    break;
            }
            return super.onTouchEvent(ev);
        }
    
        //滚动改变(拖动多少显示多少,根据手势变化)
        @Override
        protected void onScrollChanged(int l, int t, int oldl, int oldt) {
            super.onScrollChanged(l, t, oldl, oldt);
            slide.setTranslationX(l - slideWidth);
        }
    
        // 按滚动条被拖动距离判断关闭或打开菜单 (被拖动的距离有没有隐藏或显示控件的一半以上?)
        public void changeScrollx(){
            //判断拖动的距离有没有超过删除按钮的一半
            if(getScrollX() >= (slideWidth/2)){
                //推动了一半以上就打开
                this.smoothScrollTo(slideWidth, 0);
                isOpen = true;
                onSbl.onMenuIsOpen(this);
            }else{
                //没有一半以上就关上
                this.smoothScrollTo(0, 0);
                isOpen = false;
            }
        }
    
        // 关闭菜单
        public void closeMenu() {
            if (!isOpen){
                return;
            }
            this.smoothScrollTo(0, 0);
            isOpen = false;
        }
    
        //设置滑动按钮监听器
        public void setSlidingButtonListener(onSlidingButtonListener listener){
            onSbl = listener;
        }
    
        //滑动按钮侦听器
        public interface onSlidingButtonListener{
            void onMenuIsOpen(View view);
            void onDownOrMove(RecyclerItemView recycler);
        }
    }


    4、调用自定义的侧滑控件,实现单个 RecyclerView 的 xml 布局

    和 ListView 一样,需要根据需求定义一个类似于模板的 xml 布局

    这里则调用 第三步 定义的布局,实现想要的效果  item_recycler.xml :

    <?xml version="1.0" encoding="utf-8"?>
    <com.win.recyclerqq.RecyclerItemView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="@android:color/white"
        android:layout_marginBottom="1dp">
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <LinearLayout
                android:id="@+id/slide"
                android:layout_toRightOf="@+id/layout_left"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:orientation="horizontal">
    
                <TextView
                    android:id="@+id/other"
                    android:layout_height="match_parent"
                    android:layout_width="80dp"
                    android:gravity="center"
                    android:background="@drawable/button_yellow"
                    android:text="其 他" />
    
                <TextView
                    android:id="@+id/delete"
                    android:layout_height="match_parent"
                    android:layout_width="80dp"
                    android:gravity="center"
                    android:background="@drawable/button_red"
                    android:text="删 除"/>
    
            </LinearLayout>
    
            <LinearLayout
                android:id="@+id/layout_left"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/content_white"
                android:orientation="horizontal">
    
                <ImageView
                    android:id="@+id/image"
                    android:layout_width="80dp"
                    android:layout_height="50dp"
                    android:layout_gravity="center"
                    android:src="@mipmap/ic_launcher"/>
    
                <LinearLayout
                    android:layout_weight="1"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:orientation="vertical">
    
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:paddingTop="5dp"
                        android:paddingBottom="2dp"
                        android:orientation="horizontal">
    
                        <TextView
                            android:id="@+id/title"
                            android:layout_weight="1"
                            android:layout_width="0dp"
                            android:layout_height="wrap_content"
                            android:textSize="18sp"
                            android:textColor="#000000"
                            android:text="android开发交流群" />
    
                        <TextView
                            android:id="@+id/time"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:paddingRight="10dp"
                            android:paddingTop="10dp"
                            android:textColor="#747373"
                            android:textSize="12sp"
                            android:text="11:44"/>
    
                    </LinearLayout>
    
    
                    <TextView
                        android:id="@+id/content"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:textColor="#747373"
                        android:textSize="14sp"
                        android:text="广州-悠悠哉:RecyclerView很容易" />
    
                </LinearLayout>
    
            </LinearLayout>
    
        </RelativeLayout>
    
    </com.win.recyclerqq.RecyclerItemView>

    可以看到 其他删除 两个控件的位置是相对于下面的一个模板布局的,而下面的模板布局又横向占满了

    这就可以知道,在初始显示时,其他 和 删除 两个控件是被隐藏在了右边

    这个布局中有一些背景布局是自定义的布局 (

    @drawable/button_yellow
    @drawable/button_red
    @drawable/content_white
    ),详情请见:附录一


    5、写一个与自定义侧滑控件匹配的适配器

    定义一个  RecyclerViewAdapter 适配器:

    import android.content.Context;
    import android.graphics.Bitmap;
    import android.support.v7.widget.RecyclerView;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    import android.widget.Toast;
    import java.util.List;
    
    /**
     * Created by Administrator on 2018/3/16 0016.
     * item_recycler.xml 的适配器
     */
    
    public class RecyclerViewAdapter
            extends RecyclerView.Adapter<RecyclerViewAdapter.SimpleHolder>
            implements RecyclerItemView.onSlidingButtonListener{
    
        private Context context;
    
        private List<Bitmap> dataImage;    //头像(谁的头像)
        private List<String> dataTitle;     //标题(谁的消息)
        private List<String> datasContent;  //内容(消息内容)
        private List<String> datasTime;     //时间(消息时间)
    
        private onSlidingViewClickListener onSvcl;
    
        private RecyclerItemView recyclers;
    
        public RecyclerViewAdapter(Context context) {
            this.context = context;
        }
    
        public RecyclerViewAdapter(Context context,
                                   List<Bitmap> dataImage,
                                   List<String> dataTitle,
                                   List<String> datasContent,
                                   List<String> datasTime) {
            this.context = context;
            this.dataImage = dataImage;
            this.dataTitle = dataTitle;
            this.datasContent = datasContent;
            this.datasTime = datasTime;
        }
    
        @Override
        public SimpleHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(context).inflate(R.layout.item_recycler, parent, false);
            return new SimpleHolder(view);
        }
    
        @Override
        public void onBindViewHolder(final SimpleHolder holder, final int position) {
            holder.image.setImageBitmap(dataImage.get(position));
            holder.title.setText(dataTitle.get(position));
            holder.content.setText(datasContent.get(position));
            holder.time.setText(datasTime.get(position));
            holder.layout_left.getLayoutParams().width = RecyclerUtils.getScreenWidth(context);
    
            holder.layout_left.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Toast.makeText(context,"做出操作,进入新的界面或弹框",Toast.LENGTH_SHORT).show();
                    //判断是否有删除菜单打开
                    if (menuIsOpen()) {
                        closeMenu();//关闭菜单
                    } else {
                        //获得布局下标(点的哪一个)
                        int subscript = holder.getLayoutPosition();
                        onSvcl.onItemClick(view, subscript);
                    }
                }
            });
            holder.other.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Toast.makeText(context,"其他:"+position,Toast.LENGTH_SHORT).show();
                }
            });
            holder.delete.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Toast.makeText(context,"删除了:"+position,Toast.LENGTH_SHORT).show();
                    int subscript = holder.getLayoutPosition();
                    onSvcl.onDeleteBtnCilck(view,subscript);
                }
            });
        }
    
        @Override
        public int getItemCount() {
            return dataImage.size();
        }
    
        @Override
        public void onMenuIsOpen(View view) {
            recyclers = (RecyclerItemView) view;
        }
    
        @Override
        public void onDownOrMove(RecyclerItemView recycler) {
            if(menuIsOpen()){
                if(recyclers != recycler){
                    closeMenu();
                }
            }
        }
    
        class SimpleHolder extends  RecyclerView.ViewHolder {
    
            public ImageView image;
            public TextView title;
            public TextView content;
            public TextView time;
            public TextView other;
            public TextView delete;
            public LinearLayout layout_left;
            public SimpleHolder(View view) {
                super(view);
    
                image = (ImageView) view.findViewById(R.id.image);
                title = (TextView) view.findViewById(R.id.title);
                content = (TextView) view.findViewById(R.id.content);
                time = (TextView) view.findViewById(R.id.time);
                other = (TextView) view.findViewById(R.id.other);
                delete = (TextView) view.findViewById(R.id.delete);
                layout_left = (LinearLayout) view.findViewById(R.id.layout_left);
    
                ((RecyclerItemView)view).setSlidingButtonListener(RecyclerViewAdapter.this);
            }
        }
    
        //删除数据
        public void removeData(int position){
            dataImage.remove(position);
    //        notifyDataSetChanged();
            notifyItemRemoved(position);
    
        }
    
        //关闭菜单
        public void closeMenu() {
            recyclers.closeMenu();
            recyclers = null;
    
        }
    
        // 判断是否有菜单打开
        public Boolean menuIsOpen() {
            if(recyclers != null){
                return true;
            }
            return false;
        }
    
        //设置在滑动侦听器上
        public void setOnSlidListener(onSlidingViewClickListener listener) {
            onSvcl = listener;
        }
    
        // 在滑动视图上单击侦听器
        public interface onSlidingViewClickListener {
            void onItemClick(View view, int position);
            void onDeleteBtnCilck(View view, int position);
        }
    
    }

    在这里实现了各个控件的控制效果,其中:

    RecyclerUtils.getScreenWidth(context)
    是计算控件的宽度的,详情见:附录二


    6、在 Activity 调用并插入数据,实现效果

    最后需要在与 activity_main.xml 相关联的 Activity 中 调用适配器,并传入数据

    import android.graphics.Bitmap;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.support.v7.widget.DefaultItemAnimator;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    import android.widget.Toast;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MainActivity 
            extends AppCompatActivity 
            implements RecyclerViewAdapter.onSlidingViewClickListener{
    
        private RecyclerView recycler;              //在xml 中 RecyclerView 布局
        private RecyclerViewAdapter rvAdapter;      //item_recycler 布局的 适配器
    
        //设置数据
        private List<Bitmap> dataImage;    //头像(谁的头像)
        private List<String> dataTitle;     //标题(谁的消息)
        private List<String> datasContent;  //内容(消息内容)
        private List<String> datasTime;     //时间(消息时间)
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //初始化RecyclerView
            init();
            //将 RecyclerView 布局设置为线性布局
            recycler.setLayoutManager(new LinearLayoutManager(this));
            datas();//插入数据
            //更新界面
            updateInterface();
        }
    
        public void init(){
            recycler = (RecyclerView)findViewById(R.id.recycler);
        }
    
        public void updateInterface(){
            if (rvAdapter == null) {
                //实例化 RecyclerViewAdapter 并设置数据
                rvAdapter = new RecyclerViewAdapter(this, 
                        dataImage, dataTitle, datasContent, datasTime);
                //将适配的内容放入 mRecyclerView
                recycler.setAdapter(rvAdapter);
                //控制Item增删的动画,需要通过ItemAnimator  DefaultItemAnimator -- 实现自定义动画
                recycler.setItemAnimator(new DefaultItemAnimator());
            }else {
                //强调通过 getView() 刷新每个Item的内容
                rvAdapter.notifyDataSetChanged();
            }
            //设置滑动监听器 (侧滑)
            rvAdapter.setOnSlidListener(this);
        }
    
        //通过 position 区分点击了哪个 item
        @Override
        public void onItemClick(View view, int position) {
            //在这里可以做出一些反应(跳转界面、弹出弹框之类)
            Toast.makeText(MainActivity.this,"点击了:" + position,Toast.LENGTH_SHORT).show();
        }
    
        //点击删除按钮时,根据传入的 position 调用 RecyclerAdapter 中的 removeData() 方法
        @Override
        public void onDeleteBtnCilck(View view, int position) {
            rvAdapter.removeData(position);
        }
    
        public void datas(){
            dataImage = new ArrayList<Bitmap>();    //头像(谁的头像)
            dataTitle = new ArrayList<String>();     //标题(谁的消息)
            datasContent = new ArrayList<String>();  //内容(消息内容)
            datasTime = new ArrayList<String>();     //时间(消息时间)
            dataImage.add(RecyclerUtils.toRoundBitmap(RecyclerUtils.bitmaps(R.mipmap.a1, this)));
            dataImage.add(RecyclerUtils.toRoundBitmap(RecyclerUtils.bitmaps(R.mipmap.a2, this)));
            dataImage.add(RecyclerUtils.toRoundBitmap(RecyclerUtils.bitmaps(R.mipmap.a3, this)));
            dataImage.add(RecyclerUtils.toRoundBitmap(RecyclerUtils.bitmaps(R.mipmap.a4, this)));
            dataImage.add(RecyclerUtils.toRoundBitmap(RecyclerUtils.bitmaps(R.mipmap.a5, this)));
            dataImage.add(RecyclerUtils.toRoundBitmap(RecyclerUtils.bitmaps(R.mipmap.a6, this)));
            dataImage.add(RecyclerUtils.toRoundBitmap(RecyclerUtils.bitmaps(R.mipmap.a7, this)));
            dataImage.add(RecyclerUtils.toRoundBitmap(RecyclerUtils.bitmaps(R.mipmap.a8, this)));
            dataImage.add(RecyclerUtils.toRoundBitmap(RecyclerUtils.bitmaps(R.mipmap.a9, this)));
            dataImage.add(RecyclerUtils.toRoundBitmap(RecyclerUtils.bitmaps(R.mipmap.a10, this)));
            dataImage.add(RecyclerUtils.toRoundBitmap(RecyclerUtils.bitmaps(R.mipmap.a11, this)));
            dataImage.add(RecyclerUtils.toRoundBitmap(RecyclerUtils.bitmaps(R.mipmap.a12, this)));
            dataTitle.add("Android开发交流群");
            dataTitle.add("R语言初级入门学习");
            dataTitle.add("刘亦菲");
            dataTitle.add("策划书交流群");
            dataTitle.add("15生态宜居学院学生群");
            dataTitle.add("湘环资助 (助学贷款)");
            dataTitle.add("湘环编程研讨会");
            dataTitle.add("丰风");
            dataTitle.add("阿娇");
            dataTitle.add("图书馆流通服务交流群");
            dataTitle.add("one3胡了");
            dataTitle.add("读者协会策划部");
            datasContent.add("广州_Even:[图片]");
            datasContent.add("轻舟飘飘:auto基本不准");
            datasContent.add("不会的");
            datasContent.add("残留的余温。:分享[熊猫直播]");
            datasContent.add("刘老师:[文件]2018年6月全国大学……");
            datasContent.add("17级园林");
            datasContent.add("黄晓明:20k不到");
            datasContent.add("[文件]编程之美");
            datasContent.add("i5的处理器,比较稳定,蛮好的");
            datasContent.add("寥寥:好的,谢谢老师");
            datasContent.add("易天:阿龙还在面试呢");
            datasContent.add("策划部陈若依、:请大家把备注改好");
            datasTime.add("16:24");
            datasTime.add("14:37");
            datasTime.add("10:58");
            datasTime.add("昨天");
            datasTime.add("昨天");
            datasTime.add("昨天");
            datasTime.add("星期三");
            datasTime.add("星期三");
            datasTime.add("星期二");
            datasTime.add("星期二");
            datasTime.add("星期二");
            datasTime.add("星期一");
        }
    
    }

    在 第五步 写适配器时,就写了可以传入参数的构造方法,这时,我们只要调用这个构造方法将参数传入就好了

    现在这里传入的是固定的模拟参数、在实际应用中可以根据服务去传来的参数动态的赋予。

    下面这句代码是将 res 中的图片转换成 Bitmap 并裁切成圆,实现代码见:附录二

    RecyclerUtils.toRoundBitmap(RecyclerUtils.bitmaps(R.mipmap.a1, this))


    附录一

    三个背景布局格式都如下:

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_pressed="true">
            <layer-list >
                <item >
                    <shape >
                        <solid android:color="#FF0000"/>
                    </shape>
                </item>
                <item >
                    <shape >
                        <solid android:color="#22000000"/>
                    </shape>
                </item>
            </layer-list>
        </item>
        <item >
            <shape >
                <solid android:color="#FF0000"/>
            </shape>
        </item>
    </selector>

    只需要根据需求更改 color 的值就行了


    附录二

    获取控件宽度、转换图片格式、裁切图片这些都是在自定义的一个 Utils 类中实现的:

    import android.content.Context;
    import android.content.res.Resources;
    import android.graphics.BitmapFactory;
    import android.util.DisplayMetrics;
    import android.view.WindowManager;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.PorterDuff;
    import android.graphics.PorterDuffXfermode;
    import android.graphics.Rect;
    import android.graphics.RectF;
    
    /**
     * Created by Administrator on 2018/3/16 0016.
     * 包括获取控件宽度、切割位图(圆形)、将res里的图片转成位图
     */
    
    public class RecyclerUtils {
    
        //获取屏幕宽度
        public static int getScreenWidth(Context context) {
            //窗口管理器
            WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE );
            //显示度量
            DisplayMetrics displayMetrics = new DisplayMetrics();
            windowManager.getDefaultDisplay().getMetrics(displayMetrics);
            return displayMetrics.widthPixels; //返回屏幕宽度像素
        }
    
        //这个方法将位图切割成圆形
        public static Bitmap toRoundBitmap(Bitmap bitmap) {
            int width = bitmap.getWidth();
            int height = bitmap.getHeight();
            float roundPx;
            float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom;
            if (width <= height) {
                roundPx = width / 2;
                left = 0;
                top = 0;
                right = width;
                bottom = width;
                height = width;
                dst_left = 0;
                dst_top = 0;
                dst_right = width;
                dst_bottom = width;
            } else {
                roundPx = height / 2;
                float clip = (width - height) / 2;
                left = clip;
                right = width - clip;
                top = 0;
                bottom = height;
                width = height;
                dst_left = 0;
                dst_top = 0;
                dst_right = height;
                dst_bottom = height;
            }
    
            Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(output);
    
            final int color = 0xff424242;
            final Paint paint = new Paint();
            final Rect src = new Rect((int) left, (int) top, (int) right,
                    (int) bottom);
            final Rect dst = new Rect((int) dst_left, (int) dst_top,
                    (int) dst_right, (int) dst_bottom);
            final RectF rectF = new RectF(dst);
    
            paint.setAntiAlias(true);
    
            canvas.drawARGB(0, 0, 0, 0);
            paint.setColor(color);
    
            canvas.drawCircle(roundPx, roundPx, roundPx, paint);
    
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            canvas.drawBitmap(bitmap, src, dst, paint);
    
            return output;
        }
    
        //将res里的图片转换成位图 (裁剪需要)
        public static Bitmap bitmaps(int image,Context context){
            Resources res = context.getResources();
            Bitmap bitmap = BitmapFactory.decodeResource(res, image);
            return bitmap;
        }
    
    }


    源码:https://github.com/iscopy/RecyclerQQ



    展开全文
  • Android 高仿QQ 界面滑动效果

    千次阅读 多人点赞 2012-10-29 18:47:55
    点击或者滑动切换画面,用... 首先是布局文件: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" an

    点击或者滑动切换画面,用ViewPager实现,

    首先是布局文件:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
    	<LinearLayout
    	    android:layout_width="match_parent"
    	    android:layout_height="62dip"
    	    android:orientation="vertical" 
    	    android:background="@drawable/top_theme_blue">
    	    <LinearLayout
    		    android:layout_width="match_parent"
    		    android:layout_height="36dip"
    		    android:orientation="horizontal" 
    		    android:gravity="center_vertical">
    	        <ImageView
    	            android:id="@+id/main_avatar"
    	            android:layout_width="32dip"
    	            android:layout_height="32dip"
    	            android:src="@drawable/avatar" />
    		    <TextView 
    		        android:id="@+id/main_nick"
    	            android:layout_width="wrap_content"
    	            android:layout_height="wrap_content"
    	            android:textColor="#FFFFFF"
    	            android:text="Vestigge"/>
    		    <ImageView 
    		        android:layout_width="14dip"
    	            android:layout_height="14dip"
    	            android:src="@drawable/status_online"/>
    		</LinearLayout>
    	    <LinearLayout
    		    android:layout_width="match_parent"
    		    android:layout_height="26dip"
    		    android:orientation="horizontal" 
    		    android:gravity="bottom">
    		    <RadioGroup   
    		        android:id="@+id/main_radiogroup"
    		        android:orientation="horizontal"
    		        android:paddingTop="1dp" 
    		        android:layout_width="fill_parent"   
    		        android:layout_height="wrap_content">  
    	            <RadioButton   
    	                android:id="@+id/main_radio_recent"   
    	                android:checked="true"   
    	                android:text="动态" 
    	                android:textColor="@color/tab_text"
    	                android:drawableBottom="@drawable/top_tab_selector"
    	                style="@style/radio_style"/>      
    	            <RadioButton   
    	                android:id="@+id/main_radio_buddy" 
    	                android:text="群组"
    	                android:textColor="@color/tab_text" 
    	                android:drawableBottom="@drawable/top_tab_selector"
    	                style="@style/radio_style"/>  
    	            <RadioButton   
    	                android:id="@+id/main_radio_group"   
    	                android:text="好友" 
    	                android:textColor="@color/tab_text"
    	                android:drawableBottom="@drawable/top_tab_selector"
    	                style="@style/radio_style"
    	                android:checked="true"/>
    	            <RadioButton   
    	                android:id="@+id/main_radio_trends"   
    	                android:text="会话" 
    	                android:textColor="@color/tab_text"
    	                android:drawableBottom="@drawable/top_tab_selector"
    	                style="@style/radio_style"/>
    	        </RadioGroup>   
    		</LinearLayout>
    	</LinearLayout>
    	<android.support.v4.view.ViewPager
    	    android:id="@+id/main_viewpager"
    	    android:layout_width="match_parent"
        	android:layout_height="match_parent" >
    	</android.support.v4.view.ViewPager>   
    </LinearLayout>

    代码:

    public class MainActivity extends ActivityGroup {
    	private static final String TRENDS="动态";
    	private static final String GROUP="群组";
    	private static final String BUDDY="好友";
    	private static final String RECENT="会话";
    	private ArrayList<View> pageViews;
    	private RadioGroup radioGroup;
    	private ViewPager viewPager;
    
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            setContentView(R.layout.activity_main);
            
            initView();
            viewPager=(ViewPager) findViewById(R.id.main_viewpager);
            viewPager.setAdapter(new PagerAdapter(){
    			public int getCount() {
    				return pageViews.size();
    			}
    			public boolean isViewFromObject(View view, Object objcet) {
    				return view==objcet;
    			}
    			//这里会对需要进行水平切换的页面进行了加载和初始化 android:tileMode="repeat"
    			public Object instantiateItem(View view, int id) {
    				((ViewPager)view).addView(pageViews.get(id));
    				return pageViews.get(id);
    			}
    			public void destroyItem(View view, int id, Object arg2) {  
                    ((ViewPager) view).removeView(pageViews.get(id));  
                }
            });
            viewPager.setCurrentItem(2);//默认显示的是好友页面
            radioGroup = (RadioGroup) findViewById(R.id.main_radiogroup);
    		radioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {
    			public void onCheckedChanged(RadioGroup group, int checkedId) {
    				setClick(checkedId);
    			}
    		});
        }
        
        void initView() {
        	pageViews=new ArrayList<View>();
        	View view1 = getLocalActivityManager().startActivity(TRENDS,
        			new Intent(this, TrendsActivity.class)).getDecorView();
        	View view2 = getLocalActivityManager().startActivity(GROUP,
        			new Intent(this, GroupActivity.class)).getDecorView();
        	View view3 = getLocalActivityManager().startActivity(BUDDY,
        			new Intent(this, BuddyActivity.class)).getDecorView();
        	View view4 = getLocalActivityManager().startActivity(RECENT,
        			new Intent(this, RecentActivity.class)).getDecorView();
        	pageViews.add(0,view1);
        	pageViews.add(1,view2);
        	pageViews.add(2,view3);
        	pageViews.add(3,view4);
        }
    
    	public void setClick(int id) {
    		switch(id){
    		case R.id.main_radio_trends:
    			viewPager.setCurrentItem(0);
    			break;
    		case R.id.main_radio_group:
    			viewPager.setCurrentItem(1);
    			break;
    		case R.id.main_radio_buddy:
    			viewPager.setCurrentItem(2);
    			break;
    		case R.id.main_radio_recent:
    			viewPager.setCurrentItem(3);
    			break;
    		}
    	}
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.activity_main, menu);
            return true;
        }
    }


    展开全文
  • android高仿QQ登陆界面Demo

    千次下载 热门讨论 2012-08-16 09:13:19
    一款完全仿照最新QQ登陆的界面UI设计,同时实现了部分逻辑功能,希望可以帮到喜欢研究UI的朋友做参考
  • Android仿QQ空间界面源代码

    千次下载 热门讨论 2013-10-15 17:54:39
    Android仿QQ空间界面源代码微信底部带数目显示菜单源码
  • Android仿qq登陆界面

    千次阅读 2015-09-16 09:40:25
    新建一个项目,将资源全部copy到自己的资源...尝试将自己qq数据清除,在登陆之前有一个彩色qq界面,, 这个界面只是停留两秒左右,所以`/** * Created by Administrator on 2015/9/15. */ public class LoadAc
  • android仿qq实现自定义拍照界面CameraView
  • Android仿QQ登录界面示例,实现登录、注册功能。

    万次阅读 多人点赞 2018-08-28 17:58:22
    Android开发经常用到注册、登录功能,于是便整理出一般通用的登录界面,并实现其相应功能。供读者参阅。此项目包含三个活动,即登录,注册界面,找回密码。 首先是登录界面 activity_main.xml: &lt;?...
  • Android仿QQ界面

    千次阅读 2017-01-05 12:00:32
    Android版的QQ使用的是ViewPager实现的,主要是可以实现TabHost的界面,但功能比Tabhost更好,因为可以实现用手滑动实现界面的切换。QQ的截图如下:       下面我来实现这个效果 工程...
  • android-仿QQ界面布局

    千次阅读 2011-08-04 21:00:42
    仿QQ界面布局 老规矩。右键取得显示不出来的图片地址,利用下载工具下载这个图片。后缀改为rar即可得到源代码项目。 xmlns:android="http://schemas.andro
  • Android 仿qq聊天界面之一

    千次阅读 2015-08-31 20:52:34
    一、登录界面本来是只想仿一个qq的聊天界面的,顺便做了一个登录界面,熟悉下SharedPreferences(解释一下:SharedPreferences由于非常适合记录一些零散的简单的数据,因此登录界面的相关信息的记录就是通过它进行...
  • Android 仿QQ消息界面

    千次阅读 2016-05-12 09:31:55
    values 下面 dimens.xml "activity_horizontal_margin">16dp "activity_vertical_margin">16dp   主布局 activity_switch.xml "1.0" encoding="utf-8"?..."http://schemas.android.co
  • Android 高仿QQ 登陆界面

    万次阅读 多人点赞 2012-10-29 15:49:22
    先上图: 下面是布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent... android:background=
  • android 仿QQ登陆界面实现

    千次阅读 2016-08-13 21:42:16
    android 现在越来越火,之前大量的桌面软件,现在都开发出android版了。最近也在学习android,顺便做了几个demo.不说废话了,上图。 接下来是布局: <RelativeLayout xmlns:android=...
  • android仿qq聊天界面版带表情、相册、照相

    千次阅读 多人点赞 2014-04-12 16:46:23
    仿qq聊天界面版,可以聊天,可以发送表情,可以发送照相后的相片,可以发送相册中的照片。
  • Android Studio仿QQ界面实现简单的功能

    千次阅读 2021-06-02 09:45:03
    楼主近期在学习关于安卓中Fragment和ListView中的知识,按照老师的要求模仿一下QQ界面 要求功能 有登录界面 密码不对提示密码不对 账号密码任一为空提示用户不能为空 登录成功提示登录成功 可以实现账号密码记住...
  • android仿qq聊天界面的的布局适配器的写法

    千次阅读 热门讨论 2016-04-17 17:03:02
    周六日的生活就搭在这上面了,做了一个聊天功能的app的demo,最近什么都不想干,但是也不能放弃,打算最近不弄Android了,想去学习一下js,因为接了一个开发网站的项目,但是目前的...今天我要说的是仿qq聊天界面的实

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 48,012
精华内容 19,204
关键字:

android仿qq界面