码迷,mamicode.com
首页 > 微信 > 详细

仿微信主界面

时间:2015-03-30 15:59:23      阅读:469      评论:0      收藏:0      [点我收藏+]

标签:

跟着慕课网的教学视频学习了如何制作微信的主界面,因为还有一些地方并没有完全搞懂,所以这里主要是记录下整个制作的过程,方便以后的学习!

效果图如图所示:

技术分享技术分享

实现了点击下面tab切换fragment以及滑动切换tab的功能,同时滑动时,下面tab的icon会实现颜色渐变的效果。

首先是主界面的布局:

技术分享
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bunschen="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="@drawable/tab_bg"
        android:orientation="horizontal">

        <com.chen.weixin_6_0.ChangeIconColorWithText
            android:id="@+id/tab_indicator_one"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            bunschen:Icon="@drawable/ic_menu_start_conversation"
            bunschen:color="#FF008901"
            bunschen:text="@string/app_name"
            bunschen:text_size="12sp"/>

        <com.chen.weixin_6_0.ChangeIconColorWithText
            android:id="@+id/tab_indicator_two"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            bunschen:Icon="@drawable/ic_menu_friendslist"
            bunschen:color="#FF008901"
            bunschen:text="@string/tab_contact"
            bunschen:text_size="12sp"/>

        <com.chen.weixin_6_0.ChangeIconColorWithText
            android:id="@+id/tab_indicator_three"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent"
            bunschen:Icon="@drawable/ic_menu_emoticons"
            bunschen:color="#FF008901"
            bunschen:text="@string/tab_find"
            bunschen:text_size="12sp"/>

        <com.chen.weixin_6_0.ChangeIconColorWithText
            android:id="@+id/tab_indicator_four"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            bunschen:Icon="@drawable/ic_menu_allfriends"
            bunschen:color="#FF008901"
            bunschen:text="@string/tab_me"
            bunschen:text_size="12sp"/>

    </LinearLayout>

</LinearLayout>
activity_mian

主界面采用线型布局,上面是自定义的ActionBar,中间内容区域是ViewPager+Fragment,下面的Tab区域是一个横向线型布局,其中每个View都是通过自定义布局实现。

1.自定义ActionBar:

//是更多菜单按钮显示出来
private void setOverflowShowingAlways() {
try {
  ViewConfiguration config = ViewConfiguration.get(this);
  Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
  menuKeyField.setAccessible(true);
  menuKeyField.setBoolean(config, false);
  } catch (Exception e) {
    e.printStackTrace();
  }
}

该段是通过反射机制,将OverflowButton显示出来,因为在有菜单实体按键的手机中,屏幕中的菜单选项不会显示出来。

 1 @Override
 2     public boolean onMenuOpened(int featureId, Menu menu) {
 3         if (featureId == Window.FEATURE_ACTION_BAR && menu != null) {
 4             if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
 5                 try {
 6                     Method m = menu.getClass().getDeclaredMethod(
 7                             "setOptionalIconsVisible", Boolean.TYPE);
 8                     m.setAccessible(true);
 9                     m.invoke(menu, true);
10                 } catch (Exception e) {
11                 }
12             }
13         }
14         return super.onMenuOpened(featureId, menu);
15     }

这段也是通过反射机制将Overflow菜单展开的菜单选项中将图标也显示出来,因为默认是将Overflow菜单展开的菜单选项的突变隐藏掉的。

菜单布局:

 1 <menu xmlns:android="http://schemas.android.com/apk/res/android">
 2     <item
 3         android:id="@+id/action_settings"
 4         android:actionViewClass="android.widget.SearchView"
 5         android:icon="@drawable/actionbar_search_icon"
 6         android:showAsAction="ifRoom|collapseActionView"
 7         android:title="@string/action_search"
 8         />
 9 
10     <item
11         android:id="@+id/menu_contact"
12         android:icon="@drawable/menu_group_chat_icon"
13         android:title="@string/menu_contact"/>
14 
15     <item
16         android:id="@+id/menu_add_friend"
17         android:icon="@drawable/menu_add_icon"
18         android:title="@string/menu_add_friend"/>
19 
20     <item
21         android:id="@+id/menu_scan"
22         android:icon="@drawable/men_scan_icon"
23         android:title="@string/menu_contact"/>
24 
25     <item
26         android:id="@+id/menu_feedback"
27         android:icon="@drawable/menu_feedback_icon"
28         android:title="@string/menu_feedback"/>
29 
30 </menu>

接下来最主要的就是自定义View

首先是定义自定义的View需要的一些属性

values/attrs.xml:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <resources>
3 4 <attr name="Icon" format="reference"></attr> 5 <attr name="color" format="color"></attr> 6 <attr name="text" format="string"></attr> 7 <attr name="text_size" format="dimension"></attr> 8 9 <declare-styleable name="ChangeIconColorWithText"> 10 <attr name="Icon"></attr> 11 <attr name="color"></attr> 12 <attr name="text"></attr> 13 <attr name="text_size"></attr> 14 </declare-styleable> 15 </resources>

然后是在布局文件中使用:

1 <com.chen.weixin_6_0.ChangeIconColorWithText
2             android:id="@+id/tab_indicator_one"
3             android:layout_width="0dp"
4             android:layout_height="match_parent"
5             android:layout_weight="1"
6             bunschen:Icon="@drawable/ic_menu_start_conversation"
7             bunschen:color="#FF008901"
8             bunschen:text="@string/app_name"
9             bunschen:text_size="12sp"/>

注意这里的自定义的命名空间:

bunschen:Icon="@drawable/ic_menu_start_conversation"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bunschen="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

这里在开头自定义了命名空间,所以可以使用自定义的属性。

然后就是在构造函数中获取View:

  1 public class ChangeIconColorWithText extends View {
  2 
  3     private int mColor = 0xFF008901;
  4     private Bitmap mIconBitmap;
  5     private String mText = "微信";
  6     private int mTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
  7             12, getResources().getDisplayMetrics());
  8 
  9     private Bitmap mBitmap;
 10     private Canvas mCanvas;
 11     private Paint mPaint;
 12     private float alpha;
 13     private Rect mTextBounds;
 14     private Rect mBitmapBounds;
 15     private Paint textPaint;
 16 
 17     private final static String INSTANCE_STATUS = "instance_status";
 18     private final static String ALPHA_STATUS = "alpha_status";
 19 
 20     public ChangeIconColorWithText(Context context) {
 21         this(context, null);
 22     }
 23 
 24     public ChangeIconColorWithText(Context context, AttributeSet attrs) {
 25         this(context, attrs, 0);
 26     }
 27 
 28     public ChangeIconColorWithText(Context context, AttributeSet attrs, int defStyleAttr) {
 29         super(context, attrs, defStyleAttr);
 30         //获取到布局文件中定义的自定义控件的属性
 31         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ChangeIconColorWithText);
 32         int n = typedArray.getIndexCount();
 33         //将这些属性赋值给该控件的成员变量
 34         for (int i = 0; i < n; i++) {
 35             int attr = typedArray.getIndex(i);
 36             switch (attr) {
 37                 case R.styleable.ChangeIconColorWithText_color:
 38                     mColor = typedArray.getColor(attr, 0xFF0E4010);
 39                     break;
 40                 case R.styleable.ChangeIconColorWithText_Icon:
 41                     BitmapDrawable drawable = (BitmapDrawable) typedArray.getDrawable(attr);
 42                     mIconBitmap = drawable.getBitmap();
 43                     break;
 44                 case R.styleable.ChangeIconColorWithText_text:
 45                     mText = typedArray.getString(attr);
 46                     break;
 47                 case R.styleable.ChangeIconColorWithText_text_size:
 48                     mTextSize = (int) typedArray.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
 49                             12, getResources().getDisplayMetrics()));
 50                     break;
 51             }
 52         }
 53         //回收掉使用的资源
 54         typedArray.recycle();
 55         
 56         mTextBounds = new Rect();
 57         textPaint = new Paint();
 58         textPaint.setTextSize(mTextSize);
 59         textPaint.setColor(0xff555555);
 60         textPaint.getTextBounds(mText, 0, mText.length(), mTextBounds);
 61     }
 62 
 63     @Override
 64     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 65         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 66         //测量图标的宽度,长度与宽度一致
 67         int iconWidth = Math.min(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
 68                 getMeasuredHeight() - getPaddingBottom() - getPaddingTop() - mTextBounds.height());
 69         //测量图标绘制的位置的上下左右的值
 70         int left = (getMeasuredWidth() - iconWidth)/2;
 71         int top = (getMeasuredHeight() - mTextBounds.height() - iconWidth)/2;
 72         //确定icon绘制的边界
 73         mBitmapBounds = new Rect(left,top,left+iconWidth,top+iconWidth);
 74     }
 75 
 76     @Override
 77     protected void onDraw(Canvas canvas) {
 78         super.onDraw(canvas);
 79         //绘制出原无颜色的图标
 80         canvas.drawBitmap(mIconBitmap,null,mBitmapBounds,null);
 81         //ceil() 方法执行的是向上取整计算,它返回的是大于或等于函数参数,并且与之最接近的整数
 82         int Alpha = (int) Math.ceil(255 * alpha);
 83         // 内存去准备mBitmap , setAlpha , 纯色 ,xfermode , 图标
 84         setupTargetBitmap(Alpha);
 85         //1.绘制原文本。
 86         setupSourceText(canvas,Alpha);
 87         //2.绘制变色文本
 88         setupTargetText(canvas,Alpha);
 89         //将内存中绘制出的Bitmap对象绘制出来
 90         canvas.drawBitmap(mBitmap,0,0,null);
 91     }
 92     //绘制带颜色的文本
 93     private void setupTargetText(Canvas canvas, int alpha) {
 94         textPaint.setColor(mColor);
 95         textPaint.setAlpha(alpha);
 96         //计算文本绘制的位置
 97         float x = (getMeasuredWidth() - mTextBounds.width())/2;
 98         float y = (mBitmapBounds.bottom + mTextBounds.height());
 99         canvas.drawText(mText,x,y,textPaint);
100     }
101     //绘制原文本
102     private void setupSourceText(Canvas canvas, int alpha) {
103         textPaint.setAlpha(255 - alpha);
104         textPaint.setColor(0xff333333);
105         float x = (getMeasuredWidth() - mTextBounds.width())/2;
106         float y = (mBitmapBounds.bottom + mTextBounds.height());
107         canvas.drawText(mText,x,y,textPaint);
108     }
109     //在内存中绘制出icon
110     private void setupTargetBitmap(int alpha) {
111         mBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
112                 Bitmap.Config.ARGB_8888);
113         mCanvas = new Canvas(mBitmap);
114         mPaint = new Paint();
115         mPaint.setColor(mColor);
116         mPaint.setAntiAlias(true);
117         mPaint.setDither(true);
118         mPaint.setAlpha(alpha);
119         mCanvas.drawRect(mBitmapBounds, mPaint);
120         //设置显示纯色区域与图标的交集区域,即显示的是图标以及颜色为纯色区域的颜色
121         mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
122         mPaint.setAlpha(255);
123         mCanvas.drawBitmap(mIconBitmap, null, mBitmapBounds, mPaint);
124     }
125     //设置alpha值
126     public void setAlphaView(float alpha){
127         this.alpha = alpha;
128         invalidateView();
129     }
130     //当alpha值变化时,重绘视图
131     private void invalidateView() {
132         //判断是否是在UI线程
133         if(Looper.getMainLooper() == Looper.myLooper()){
134             invalidate();
135         }else{
136             postInvalidate();
137         }
138     }
139     //保存数据值及状态,防止Activity被系统销毁时在回到主界面时显示不正常的现象
140     @Override
141     protected Parcelable onSaveInstanceState() {
142         Bundle bundle = new Bundle();
143         bundle.putParcelable(INSTANCE_STATUS,super.onSaveInstanceState());
144         bundle.putFloat(ALPHA_STATUS,alpha);
145         return bundle;
146     }
147     //回复原先保存的数据值及状态
148     @Override
149     protected void onRestoreInstanceState(Parcelable state) {
150         if(state instanceof Bundle){
151             Bundle bundle = (Bundle) state;
152             alpha = bundle.getFloat(ALPHA_STATUS);
153             super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATUS));
154             return;
155         }
156         super.onRestoreInstanceState(state);
157     }
158 }

然后是在MainActivity中实现滑动更新tab,以及点击tab更新fragment的逻辑:

  1 public class MainActivity extends FragmentActivity implements View.OnClickListener, ViewPager.OnPageChangeListener {
  2 
  3     private ViewPager viewPager;
  4     //fragment中显示的文本内容
  5     private String[] mTitles = new String[]{"first tab fragment", "second tab fragment",
  6             "third tab fragment", "fourth tab fragment"};
  7 
  8     private FragmentPagerAdapter mAdapter;
  9 
 10     private List<Fragment> mData = new ArrayList<>();
 11     //管理四个tab的List集合
 12     private List<ChangeIconColorWithText> tabList = new ArrayList<>();
 13 
 14     @Override
 15     protected void onCreate(Bundle savedInstanceState) {
 16         super.onCreate(savedInstanceState);
 17         setContentView(R.layout.activity_main);
 18         setOverflowShowingAlways();
 19         getActionBar().setDisplayHomeAsUpEnabled(false);
 20 
 21         initView();
 22         initData();
 23         viewPager.setAdapter(mAdapter);
 24         viewPager.setOnPageChangeListener(this);
 25     }
 26 
 27     private void initData() {
 28         for(String title : mTitles){
 29             TabFragment tabFragment = new TabFragment();
 30             Bundle bundle = new Bundle();
 31             bundle.putString(TabFragment.TITLE,title);
 32             tabFragment.setArguments(bundle);
 33             mData.add(tabFragment);
 34         }
 35 
 36         mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
 37             @Override
 38             public Fragment getItem(int position) {
 39                 return mData.get(position);
 40             }
 41 
 42             @Override
 43             public int getCount() {
 44                 return mData.size();
 45             }
 46         };
 47     }
 48 
 49     private void initView() {
 50         viewPager = (ViewPager) findViewById(R.id.viewPager);
 51         ChangeIconColorWithText one = (ChangeIconColorWithText) findViewById(R.id.tab_indicator_one);
 52         tabList.add(one);
 53         ChangeIconColorWithText two = (ChangeIconColorWithText) findViewById(R.id.tab_indicator_two);
 54         tabList.add(two);
 55         ChangeIconColorWithText three = (ChangeIconColorWithText) findViewById(R.id.tab_indicator_three);
 56         tabList.add(three);
 57         ChangeIconColorWithText four = (ChangeIconColorWithText) findViewById(R.id.tab_indicator_four);
 58         tabList.add(four);
 59 
 60         one.setOnClickListener(this);
 61         two.setOnClickListener(this);
 62         three.setOnClickListener(this);
 63         four.setOnClickListener(this);
 64         resetOtherTab();
 65         one.setAlphaView(1);
 66     }
 67 
 68 
 69     @Override
 70     public boolean onCreateOptionsMenu(Menu menu) {
 71         getMenuInflater().inflate(R.menu.menu_main, menu);
 72 
 73         return true;
 74     }
 75 
 76     @Override
 77     public boolean onOptionsItemSelected(MenuItem item) {
 78         int id = item.getItemId();
 79 
 80         if (id == R.id.action_settings) {
 81             return true;
 82         }
 83         return super.onOptionsItemSelected(item);
 84     }
 85 
 86     //是更多菜单按钮显示出来
 87     private void setOverflowShowingAlways() {
 88         try {
 89             ViewConfiguration config = ViewConfiguration.get(this);
 90             Field menuKeyField = ViewConfiguration.class
 91                     .getDeclaredField("sHasPermanentMenuKey");
 92             menuKeyField.setAccessible(true);
 93             menuKeyField.setBoolean(config, false);
 94         } catch (Exception e) {
 95             e.printStackTrace();
 96         }
 97     }
 98 
 99     @Override
100     public boolean onMenuOpened(int featureId, Menu menu) {
101         if (featureId == Window.FEATURE_ACTION_BAR && menu != null) {
102             if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
103                 try {
104                     Method m = menu.getClass().getDeclaredMethod(
105                             "setOptionalIconsVisible", Boolean.TYPE);
106                     m.setAccessible(true);
107                     m.invoke(menu, true);
108                 } catch (Exception e) {
109                 }
110             }
111         }
112         return super.onMenuOpened(featureId, menu);
113     }
114 
115     @Override
116     public void onClick(View v) {
117         resetOtherTab();
118         switch(v.getId()){
119             case R.id.tab_indicator_one:
120                 tabList.get(0).setAlphaView(1);
121                 viewPager.setCurrentItem(0,false);
122                 break;
123             case R.id.tab_indicator_two:
124                 tabList.get(1).setAlphaView(1);
125                 viewPager.setCurrentItem(1,false);
126                 break;
127             case R.id.tab_indicator_three:
128                 tabList.get(2).setAlphaView(1);
129                 viewPager.setCurrentItem(2,false);
130                 break;
131             case R.id.tab_indicator_four:
132                 tabList.get(3).setAlphaView(1);
133                 viewPager.setCurrentItem(3,false);
134                 break;
135         }
136     }
137 
138     private void resetOtherTab() {
139         for(int i = 0; i < tabList.size(); i++){
140             tabList.get(i).setAlphaView(0);
141         }
142     }
143   //这里是在ViewPager滑动时,因为只有两个tab的颜色会发生变化,所以通过将他们的icon和文本颜色的alpha值进行改变,从而产生渐变的效果。
144     @Override
145     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
146         if(positionOffset > 0){
147             ChangeIconColorWithText left = tabList.get(position);
148             ChangeIconColorWithText right = tabList.get(position + 1);
149 
150             left.setAlphaView(1-positionOffset);
151             right.setAlphaView(positionOffset);
152         }
153     }
154 
155     @Override
156     public void onPageSelected(int position) {
157 
158     }
159 
160     @Override
161     public void onPageScrollStateChanged(int state) {
162 
163     }

fragment:

 1 public class TabFragment extends Fragment {
 2 
 3     private static String mTitle = "default";
 4     public static final String TITLE = "title";
 5     @Override
 6     public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState) {
 7         TextView tv = new TextView(getActivity());
 8         if(getArguments() != null) {
 9             mTitle = getArguments().getString(TITLE);
10         }
11         tv.setText(mTitle);
12         tv.setTextSize(20);
13         tv.setTextColor(Color.BLACK);
14         tv.setGravity(Gravity.CENTER);
15         return tv;
16     }
17 }

 基本内容就是这些,其中自定义View是难点,主要是自定义View中的绘制方法,XferMode的DST_IN方法。这里记录下来,以后慢慢学习。

仿微信主界面

标签:

原文地址:http://www.cnblogs.com/bunschen/p/4378212.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!