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


实现了点击下面tab切换fragment以及滑动切换tab的功能,同时滑动时,下面tab的icon会实现颜色渐变的效果。
首先是主界面的布局:
主界面采用线型布局,上面是自定义的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显示出来,因为在有菜单实体按键的手机中,屏幕中的菜单选项不会显示出来。
//是更多菜单按钮显示出来
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();
}
}
这段也是通过反射机制将Overflow菜单展开的菜单选项中将图标也显示出来,因为默认是将Overflow菜单展开的菜单选项的突变隐藏掉的。
菜单布局:
接下来最主要的就是自定义View
首先是定义自定义的View需要的一些属性
values/attrs.xml:
然后是在布局文件中使用:
注意这里的自定义的命名空间:
bunschen:Icon="@drawable/ic_menu_start_conversation"
这里在开头自定义了命名空间,所以可以使用自定义的属性。
然后就是在构造函数中获取View:
public class ChangeIconColorWithText extends View {
private int mColor = 0xFF008901;
private Bitmap mIconBitmap;
private String mText = "微信";
private int mTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
12, getResources().getDisplayMetrics());
private Bitmap mBitmap;
private Canvas mCanvas;
private Paint mPaint;
private float alpha;
private Rect mTextBounds;
private Rect mBitmapBounds;
private Paint textPaint;
private final static String INSTANCE_STATUS = "instance_status";
private final static String ALPHA_STATUS = "alpha_status";
public ChangeIconColorWithText(Context context) {
this(context, null);
}
public ChangeIconColorWithText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ChangeIconColorWithText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取到布局文件中定义的自定义控件的属性
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ChangeIconColorWithText);
int n = typedArray.getIndexCount();
//将这些属性赋值给该控件的成员变量
for (int i = 0; i < n; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.ChangeIconColorWithText_color:
mColor = typedArray.getColor(attr, 0xFF0E4010);
break;
case R.styleable.ChangeIconColorWithText_Icon:
BitmapDrawable drawable = (BitmapDrawable) typedArray.getDrawable(attr);
mIconBitmap = drawable.getBitmap();
break;
case R.styleable.ChangeIconColorWithText_text:
mText = typedArray.getString(attr);
break;
case R.styleable.ChangeIconColorWithText_text_size:
mTextSize = (int) typedArray.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
12, getResources().getDisplayMetrics()));
break;
}
}
//回收掉使用的资源
typedArray.recycle();
mTextBounds = new Rect();
textPaint = new Paint();
textPaint.setTextSize(mTextSize);
textPaint.setColor(0xff555555);
textPaint.getTextBounds(mText, 0, mText.length(), mTextBounds);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//测量图标的宽度,长度与宽度一致
int iconWidth = Math.min(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
getMeasuredHeight() - getPaddingBottom() - getPaddingTop() - mTextBounds.height());
//测量图标绘制的位置的上下左右的值
int left = (getMeasuredWidth() - iconWidth)/2;
int top = (getMeasuredHeight() - mTextBounds.height() - iconWidth)/2;
//确定icon绘制的边界
mBitmapBounds = new Rect(left,top,left+iconWidth,top+iconWidth);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制出原无颜色的图标
canvas.drawBitmap(mIconBitmap,null,mBitmapBounds,null);
//ceil() 方法执行的是向上取整计算,它返回的是大于或等于函数参数,并且与之最接近的整数
int Alpha = (int) Math.ceil(255 * alpha);
// 内存去准备mBitmap , setAlpha , 纯色 ,xfermode , 图标
setupTargetBitmap(Alpha);
//1.绘制原文本。
setupSourceText(canvas,Alpha);
//2.绘制变色文本
setupTargetText(canvas,Alpha);
//将内存中绘制出的Bitmap对象绘制出来
canvas.drawBitmap(mBitmap,0,0,null);
}
//绘制带颜色的文本
private void setupTargetText(Canvas canvas, int alpha) {
textPaint.setColor(mColor);
textPaint.setAlpha(alpha);
//计算文本绘制的位置
float x = (getMeasuredWidth() - mTextBounds.width())/2;
float y = (mBitmapBounds.bottom + mTextBounds.height());
canvas.drawText(mText,x,y,textPaint);
}
//绘制原文本
private void setupSourceText(Canvas canvas, int alpha) {
textPaint.setAlpha(255 - alpha);
textPaint.setColor(0xff333333);
float x = (getMeasuredWidth() - mTextBounds.width())/2;
float y = (mBitmapBounds.bottom + mTextBounds.height());
canvas.drawText(mText,x,y,textPaint);
}
//在内存中绘制出icon
private void setupTargetBitmap(int alpha) {
mBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mPaint = new Paint();
mPaint.setColor(mColor);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setAlpha(alpha);
mCanvas.drawRect(mBitmapBounds, mPaint);
//设置显示纯色区域与图标的交集区域,即显示的是图标以及颜色为纯色区域的颜色
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mPaint.setAlpha(255);
mCanvas.drawBitmap(mIconBitmap, null, mBitmapBounds, mPaint);
}
//设置alpha值
public void setAlphaView(float alpha){
this.alpha = alpha;
invalidateView();
}
//当alpha值变化时,重绘视 private void invalidateView() {
//判断是否是在UI线程
if(Looper.getMainLooper() == Looper.myLooper()){
invalidate();
}else{
postInvalidate();
}
}
//保存数据值及状态,防止Activity被系统销毁时在回到主界面时显示不正常的现象
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(INSTANCE_STATUS,super.onSaveInstanceState());
bundle.putFloat(ALPHA_STATUS,alpha);
return bundle;
}
//回复原先保存的数据值及状态
@Override
protected void onRestoreInstanceState(Parcelable state) {
if(state instanceof Bundle){
Bundle bundle = (Bundle) state;
alpha = bundle.getFloat(ALPHA_STATUS);
super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATUS));
return;
}
super.onRestoreInstanceState(state);
}
}
然后是在MainActivity中实现滑动更新tab,以及点击tab更新fragment的逻辑:
public class MainActivity extends FragmentActivity implements View.OnClickListener, ViewPager.OnPageChangeListener {
private ViewPager viewPager;
//fragment中显示的文本内容
private String[] mTitles = new String[]{"first tab fragment", "second tab fragment",
"third tab fragment", "fourth tab fragment"};
private FragmentPagerAdapter mAdapter;
private List mData = new ArrayList<>();
//管理四个tab的List集合
private List tabList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setOverflowShowingAlways();
getActionBar().setDisplayHomeAsUpEnabled(false);
initView();
initData();
viewPager.setAdapter(mAdapter);
viewPager.setOnPageChangeListener(this);
}
private void initData() {
for(String title : mTitles){
TabFragment tabFragment = new TabFragment();
Bundle bundle = new Bundle();
bundle.putString(TabFragment.TITLE,title);
tabFragment.setArguments(bundle);
mData.add(tabFragment);
}
mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
return mData.get(position);
}
@Override
public int getCount() {
return mData.size();
}
};
}
private void initView() {
viewPager = (ViewPager) findViewById(R.id.viewPager);
ChangeIconColorWithText one = (ChangeIconColorWithText) findViewById(R.id.tab_indicator_one);
tabList.add(one);
ChangeIconColorWithText two = (ChangeIconColorWithText) findViewById(R.id.tab_indicator_two);
tabList.add(two);
ChangeIconColorWithText three = (ChangeIconColorWithText) findViewById(R.id.tab_indicator_three);
tabList.add(three);
ChangeIconColorWithText four = (ChangeIconColorWithText) findViewById(R.id.tab_indicator_four);
tabList.add(four);
one.setOnClickListener(this);
two.setOnClickListener(this);
three.setOnClickListener(this);
four.setOnClickListener(this);
resetOtherTab();
one.setAlphaView(1);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
//是更多菜单按钮显示出来
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();
}
}
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
if (featureId == Window.FEATURE_ACTION_BAR && menu != null) {
if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
try {
Method m = menu.getClass().getDeclaredMethod(
"setOptionalIconsVisible", Boolean.TYPE);
m.setAccessible(true);
m.invoke(menu, true);
} catch (Exception e) {
}
}
}
return super.onMenuOpened(featureId, menu);
}
@Override
public void onClick(View v) {
resetOtherTab();
switch(v.getId()){
case R.id.tab_indicator_one:
tabList.get(0).setAlphaView(1);
viewPager.setCurrentItem(0,false);
break;
case R.id.tab_indicator_two:
tabList.get(1).setAlphaView(1);
viewPager.setCurrentItem(1,false);
break;
case R.id.tab_indicator_three:
tabList.get(2).setAlphaView(1);
viewPager.setCurrentItem(2,false);
break;
case R.id.tab_indicator_four:
tabList.get(3).setAlphaView(1);
viewPager.setCurrentItem(3,false);
break;
}
}
private void resetOtherTab() { for(int i = 0; i < tabList.size(); i++){
tabList.get(i).setAlphaView(0);
}
}
//这里是在ViewPager滑动时,因为只有两个tab的颜色会发生变化,所以通过将他们的icon和文本颜色的alpha值进行改变,从而产生渐变的效果。
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if(positionOffset > 0){
ChangeIconColorWithText left = tabList.get(position);
ChangeIconColorWithText right = tabList.get(position + 1);
left.setAlphaView(1-positionOffset);
right.setAlphaView(positionOffset);
}
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
fragment:
public class TabFragment extends Fragment {
private static String mTitle = "default";
public static final String TITLE = "title";
@Override
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState) {
TextView tv = new TextView(getActivity());
if(getArguments() != null) {
mTitle = getArguments().getString(TITLE);
}
tv.setText(mTitle);
tv.setTextSize(20);
tv.setTextColor(Color.BLACK);
tv.setGravity(Gravity.CENTER);
return tv;
}
}
基本内容就是这些,其中自定义View是难点,主要是自定义View中的绘制方法,XferMode的DST_IN方法。这里记录下来,以后慢慢学习。





