标签:
跟着慕课网的教学视频学习了如何制作微信的主界面,因为还有一些地方并没有完全搞懂,所以这里主要是记录下整个制作的过程,方便以后的学习!
效果图如图所示:
实现了点击下面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>
主界面采用线型布局,上面是自定义的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