前面一篇文章里面,我们谈及到了微信5.2.1,并对其进行了简单的实现。但存在了两个问题:
1.在onPageScrolled()函数中写了很多判断,如果仔细观察,其实不用写的那么复杂。
2.底边文字和图片滑动页面时候,如何跟进滑动过程完成颜色的渐变。
这次我们来进行一一解答,本篇博客主要解决了三个问题:
1.使用ActionBar高仿微信顶部菜单。
2.自定义控件,使用setXfermode完成底部菜单拖动的过程中,完成文字和图片等渐变效果。
3.简化onPageScrolled()方法。
效果如下:
以下为相关代码:
menu_main.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<item android:id="@+id/action_search"
android:actionViewClass="android.widget.SearchView"
android:icon="@mipmap/actionbar_search_icon"
android:showAsAction="ifRoom|collapseActionView"
android:title="@string/action_search"
tools:ignore="AppCompatResource"
/>
<item android:id="@+id/action_group_chat"
android:icon="@mipmap/menu_group_chat_icon"
android:title="@string/menu_group_chat"
/>
<item android:id="@+id/action_add_friends"
android:icon="@mipmap/menu_add_icon"
android:title="@string/menu_addfriends"
/>
<item android:id="@+id/action_scan"
android:icon="@mipmap/men_scan_icon"
android:title="@string/menu_sacn" />
<item android:id="@+id/action_feedback"
android:icon="@mipmap/menu_feedback_icon"
android:title="@string/menu_feedback"
/>
</menu>
上面为menu的相关布局,第一个item为点击搜索,后面四个item为点击加号展现的菜单。
以下为主界面MainActivity的activity_main.xml布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="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/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.imooc.weixin.ChangeColorIconWithText
android:id="@+id/id_indicator_one"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:padding="5dp"
app:tab_color="#ff45c01a"
app:tab_icon="@mipmap/ic_menu_start_conversation"
app:text="@string/app_name"
app:text_size="12sp" />
<com.imooc.weixin.ChangeColorIconWithText
android:id="@+id/id_indicator_two"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:padding="5dp"
app:tab_color="#ff45c01a"
app:tab_icon="@mipmap/ic_menu_allfriends"
app:text="@string/tab_contact"
app:text_size="12sp" />
<com.imooc.weixin.ChangeColorIconWithText
android:id="@+id/id_indicator_three"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:padding="5dp"
app:tab_color="#ff45c01a"
app:tab_icon="@mipmap/ic_menu_emoticons"
app:text="@string/tab_found"
app:text_size="12sp" />
<com.imooc.weixin.ChangeColorIconWithText
android:id="@+id/id_indicator_four"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:padding="5dp"
app:tab_color="#ff45c01a"
app:tab_icon="@mipmap/ic_menu_allfriends"
app:text="@string/tab_me"
app:text_size="12sp" />
</LinearLayout>
</LinearLayout>
整个为一个线性布局,上面是一个ViewPager,下面是自定义的四个view,即我们底部的四个tab,关于四个tab我们自定义了四个属性:tab_color,tab_icon,tab_text,text_size。
下面是关于自定义属性的attr.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="tab_icon" format="reference" />
<attr name="tab_color" format="color" />
<attr name="text" format="string" />
<attr name="text_size" format="dimension" />
<declare-styleable name="ChangeColorIconWithText">
<attr name="tab_icon" />
<attr name="tab_color" />
<attr name="text" />
<attr name="text_size" />
</declare-styleable>
</resources>
下面就来到了我们的核心代码部分,第一部分为MainActivity.java:
package com.imooc.weixin;import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.Menu;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.Window;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends FragmentActivity implements View.OnClickListener, ViewPager.OnPageChangeListener {
private ViewPager mViewPager; //初始化ViewPager
private List<Fragment> mTabs = new ArrayList<>();//Fragment集合
private String[] mTitles = new String[]{
"First Fragment!", "Second Fragment!",
"Third Fragment!", "Fourth Fragment!" }; //4个tab点击或者滑动对应的Fragment中的text
private FragmentPagerAdapter mAdapter; //适配器
private List<ChangeColorIconWithText> mTabsIndictor=new ArrayList<>();//tab集
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/***
右侧加号在部分机器上不会显示,所以这里反射,强制
让其显示
***/
setOverflowButtonAlways();
if (null != getActionBar()) {
/*** 隐藏最右侧默认的三个点的图标 ***/
getActionBar().setDisplayShowHomeEnabled(false);
}
/*** 初始化view ***/
initView();
/*** 初始化数据 ***/
initDatas();
mViewPager.setAdapter(mAdapter);
/*** 初始化触摸事件 ***/
initEvent();
}
private void initEvent() {
mViewPager.addOnPageChangeListener(this);
}
/*** 跟进Tab数量出事后TabFragement ***/
private void initDatas() {
for (String title : mTitles) {
TabFragment tabFragment = new TabFragment();
Bundle bundle = new Bundle();
bundle.putString(TabFragment.TITLE, title);
tabFragment.setArguments(bundle);
mTabs.add(tabFragment);
}
mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int i) {
return mTabs.get(i);
}
@Override
public int getCount() {
return mTabs.size();
}
};
}
/*** 初始化了四个ChangeColorIconWithText 四个tab我们默认显示第一个 所以将第一个tab的alpha设置了为1.0f 其他的tab为0.0f 则默认显示原色 即不是选中状态 ***/
private void initView() {
mViewPager = (ViewPager) findViewById(R.id.id_viewpager);
ChangeColorIconWithText id_indicator_one = (ChangeColorIconWithText) this.findViewById(R.id.id_indicator_one);
ChangeColorIconWithText id_indicator_two = (ChangeColorIconWithText) this.findViewById(R.id.id_indicator_two);
ChangeColorIconWithText id_indicator_three = (ChangeColorIconWithText) this.findViewById(R.id.id_indicator_three);
ChangeColorIconWithText id_indicator_four = (ChangeColorIconWithText) this.findViewById(R.id.id_indicator_four);
mTabsIndictor.add(id_indicator_one);
mTabsIndictor.add(id_indicator_two);
mTabsIndictor.add(id_indicator_three);
mTabsIndictor.add(id_indicator_four);
id_indicator_one.setOnClickListener(this);
id_indicator_two.setOnClickListener(this);
id_indicator_three.setOnClickListener(this);
id_indicator_four.setOnClickListener(this);
id_indicator_one.setIconAlpha(1.0F);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
/*** 设置显示加号 ***/
private void setOverflowButtonAlways() {
try {
ViewConfiguration config = ViewConfiguration.get(this);
Field menuKey = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
menuKey.setAccessible(true);
menuKey.setBoolean(config, false);
} catch (Exception e) {
e.printStackTrace();
}
}
/*** 显示菜单前面的icon ***/
@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) {
e.printStackTrace();
}
}
}
return super.onMenuOpened(featureId, menu);
}
@Override
public void onClick(View v) {
/*** 每次点击tab 重置tab状态 ***/
resetOtherTabs();
switch (v.getId())
{
case R.id.id_indicator_one:
/*** 改变tab选中颜色 ***/
mTabsIndictor.get(0).setIconAlpha(1.0f);
/*** 点击tab 设置相应的Fragment显示 false为去掉了切换适配器Fragment的动画效果 ***/
mViewPager.setCurrentItem(0,false);
break;
case R.id.id_indicator_two:
mTabsIndictor.get(1).setIconAlpha(1.0f);
mViewPager.setCurrentItem(1,false);
break;
case R.id.id_indicator_three:
mTabsIndictor.get(2).setIconAlpha(1.0f);
mViewPager.setCurrentItem(2,false);
break;
case R.id.id_indicator_four:
mTabsIndictor.get(3).setIconAlpha(1.0f);
mViewPager.setCurrentItem(3,false);
break;
}
}
/*** 重置其他的Tab颜色 */
private void resetOtherTabs() {
for (int i=0;i<mTabsIndictor.size();i++) {
mTabsIndictor.get(i).setIconAlpha(0);
}
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
/*** 如果positionOffset满足大于0的条件 下面我举例来说明这个地方 比如是从0滑倒1 position为0 那么右侧为position+1 positionOffset的值是不断增大的 使用所以left.setIconAlpha会越来越小 right.setIconAlpha会越来越大 同理 ***/
if (positionOffset>0)
{
ChangeColorIconWithText left=mTabsIndictor.get(position);
ChangeColorIconWithText right=mTabsIndictor.get(position+1);
left.setIconAlpha(1-positionOffset);
right.setIconAlpha(positionOffset);
}
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state)
{
}
}
下面就是来到了我们的关键部分也就是我们自定义的一个ChangeColorIconWithText,下面我们来看下它:
package com.imooc.weixin;
import android.content.Context;
import android.content.res.TypedArray;
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.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Looper;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
/** * Author:${luomingjun}
* 时间:2016/6/22 16:25
*/
public class ChangeColorIconWithText extends View {
private int mTab_Color = 0xFF45C01A;
private Bitmap mIconBitmap;
private String mText = "微信";
private int mTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics());
private Canvas mCanvas;
private Bitmap mBitmap;
private Paint mPaint;
private float mAlpha;
private Rect mIconRect;
private Rect mTextBound;
private Paint mTextPaint;
public ChangeColorIconWithText(Context context) {
this(context, null);
}
public ChangeColorIconWithText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* 获取自定义属性的值
*
* @param context 上下文参数
* @param attrs AttributeSet
* @param defStyleAttr defStyleAttr
*/
public ChangeColorIconWithText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ChangeColorIconWithText);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.ChangeColorIconWithText_tab_icon:
BitmapDrawable bitmapDrawable = (BitmapDrawable) a.getDrawable(attr);
mIconBitmap = bitmapDrawable.getBitmap();
break;
case R.styleable.ChangeColorIconWithText_text:
mText = a.getString(attr);
break;
case R.styleable.ChangeColorIconWithText_tab_color:
mTab_Color = a.getColor(attr, 0xFF45C01A); break;
case R.styleable.ChangeColorIconWithText_text_size:
mTextSize = (int) a.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics()));
break;
}
}
a.recycle(); //养成好的习惯
/*** 初始化文本绘制相关属性 ***/
mTextBound = new Rect();
mTextPaint = new Paint();
mTextPaint.setTextSize(mTextSize);
mTextPaint.setColor(0xff555555);
mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBound);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/*** 计算绘制tab区域的大小(icon+text) ***/
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int iconWidth = Math.min(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - mTextBound.height());
int left = getMeasuredWidth() / 2 - iconWidth / 2;
int top = getMeasuredHeight() / 2 - (mTextBound.height() + iconWidth) / 2;
mIconRect = new Rect(left, top, left + iconWidth, top + iconWidth);
}
@Override
protected void onDraw(Canvas canvas) {
/*** 绘制原色的icon ***/
canvas.drawBitmap(mIconBitmap, null, mIconRect, null);
int alpha = (int) Math.ceil(255 * mAlpha);
/*** 在内存中绘制可变色的Icon ***/
setUpTargetBitmap(alpha);
/*** 绘制原色文本 ***/
drawSourceText(canvas, alpha);
/*** 绘制变色文本 ***/
drawTargetText(canvas, alpha);
/*** 绘制出客变色的icon ***/
canvas.drawBitmap(mBitmap, 0, 0, null);
}
/**
* 绘制变色的文本
*
* @param canvas
* @param alpha
*/
private void drawTargetText(Canvas canvas, int alpha) {
mTextPaint.setColor(mTab_Color);
mTextPaint.setAlpha(alpha);
int x = getMeasuredWidth() / 2 - mTextBound.width() / 2;
int y = mIconRect.bottom + mTextBound.height();
canvas.drawText(mText, x, y, mTextPaint); }
/**
* 在内存中绘制可变色的Icon
*/
private void setUpTargetBitmap(int alpha) {
mBitmap = Bitmap.createBitmap(getMeasuredWidth(),
getMeasuredHeight(), Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mPaint = new Paint();
mPaint.setColor(mTab_Color);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setAlpha(alpha);
mCanvas.drawRect(mIconRect, mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mPaint.setAlpha(255);
mCanvas.drawBitmap(mIconBitmap, null, mIconRect, mPaint);
}
/**
* 绘制原文本
*
* @param canvas canvas
* @param alpha alpha
*/
private void drawSourceText(Canvas canvas, int alpha) {
mTextPaint.setColor(0xff333333);
mTextPaint.setAlpha(255 - alpha);
int x = getMeasuredWidth() / 2 - mTextBound.width() / 2;
int y = mIconRect.bottom + mTextBound.height();
canvas.drawText(mText, x, y, mTextPaint);
}
/**
* 改变alpha值 并重新绘制
*/
public void setIconAlpha(float alpha) {
this.mAlpha = alpha;
invalidateView();
}
/**
* 重绘
*/
private void invalidateView() {
if (Looper.getMainLooper() == Looper.myLooper()) {
//Ui线程
invalidate();
} else {
postInvalidate();
}
}
/**
* 防止Activity回收之后 进去之后tab选中的颜色和fragment不对应 不做以下处理 回收之后 默认会选中第一个
*/
private static final String INSTANCE_STATUS = "instance_status";
private static final String STATUS_ALPHA = "status_alpha";
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(INSTANCE_STATUS,super.onSaveInstanceState());
bundle.putFloat(STATUS_ALPHA, mAlpha);
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle= (Bundle) state;
mAlpha=bundle.getFloat(STATUS_ALPHA);
super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATUS));
return;
}
super.onRestoreInstanceState(state);
}
}
上面有一行代码是:
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
下面我们具体来了解下,如下为Porter-Duff 效果图:
很明显,我们采用了PorterDuff.Mode.DST_IN,取绘制两层交集,显示上层:
以下博客为Porter-Duff的详细讲解:
http://blog.csdn.net/t12x3456/article/details/10432935