仿微信6.x底部指示器渐变效果

首先上效果图
这里写图片描述 这里写图片描述
大家微信都很熟悉了,这里的效果就是仿照微信,在主界面左右滑动的时候,下面的导航图标会产生渐变的效果,滑动是,之前图标会慢慢变淡,当前的图标会渐渐边成选中的颜色。

这种效果如何实现呢,当然要使用我强大的自定义view。

自定义ChangeColorIconWithText

对于自定view,我们一般需要下面几步:
1.自定义属性
在attrs.xml文件中添加下列代码。

    <attr name="icon" format="reference"></attr>
    <attr name="color" format="color"></attr>
    <attr name="text" format="string"></attr>
    <attr name="text_size" format="dimension"></attr>
    <declare-styleable name="ChangeColorIconWithText">
        <attr name="icon"></attr>
        <attr name="color"></attr>
        <attr name="text"></attr>
        <attr name="text_size"></attr>
    </declare-styleable>

2.onMeasure
确定图标位置,对于位置的技术不理解可以自行画图看看。

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int iconWith = Math.min(getMeasuredWidth()-getPaddingLeft()-getPaddingRight(),getMeasuredHeight()-getPaddingTop()-getPaddingBottom()-mTextBound.height());

        int left = getMeasuredWidth()/2 - iconWith/2;
        int top = (getMeasuredHeight()-mTextBound.height())/2-iconWith/2;
        mIconRect = new Rect(left, top, left+iconWith, top+iconWith);
    }

3.onDraw
这里现在内存中的Canvas上绘制一个可变色的bitmap,然后再将其绘制到屏幕显示的Canvas上,文字的变色与图标变色没有关系,只是两者都用相同的alpha来控制。

@Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mIconBitmap,null, mIconRect, null);
        int alpha = (int) Math.ceil(255*mAlpha);
        //在内存中准备mBitmap
        setTargetBitmap(alpha);

        //绘制原文本
        drawSourceText(canvas, alpha);
        //绘制变色的文本
        drawTargetText(canvas, alpha);

        canvas.drawBitmap(mBitmap, 0, 0, null);
    }

4.对外公布一个控制透明度的方法

    public void setIconAlpha(float alpha)
    {
        this.mAlpha = alpha;
        //重绘
        if (Looper.getMainLooper() == Looper.myLooper())//判断是否是主线程
        {
            invalidate();
        }else{
            postInvalidate();
        }
    }

5.Xfermode及PorterDuff

  1. Xfermode:
    Xfermode有三个子类 :
    AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。
    PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素异或操作。
    PorterDuffXfermode 这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。
    ChangeColorIconWithText类全部代码
  2. PorterDuff
    ProterDuff是两个人名的组合: Tomas Proter和 Tom Duff. 他们是最早在SIGGRAPH上提出图形混合概念的大神级人物.有兴趣可以自己查下并深入了解。
    对于PorterDuff.Mode有下面几种模式:
    这里写图片描述

ChangeColorIconWithText类全部代码

public class ChangeColorIconWithText extends View {

    private int mColor = 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);
    }

    public ChangeColorIconWithText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ChangeColorIconWithText);
        for (int i = 0; i < a.getIndexCount(); i++)
        {
            int attr = a.getIndex(i);
            switch (attr)
            {
                case R.styleable.ChangeColorIconWithText_icon:
                    BitmapDrawable drawable = (BitmapDrawable) a.getDrawable(attr);
                    mIconBitmap = drawable.getBitmap();
                    break;
                case R.styleable.ChangeColorIconWithText_color:
                    mColor = a.getColor(attr,0xff45c01a);
                    break;
                case R.styleable.ChangeColorIconWithText_text:
                    mText = a.getString(attr);
                    break;
                case R.styleable.ChangeColorIconWithText_text_size:
                    mTextSize = (int)a.getDimension(attr,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,12, getResources().getDisplayMetrics()));
            }
        }
        a.recycle();

        mTextBound = new Rect();
        mTextPaint = new Paint();
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setColor(0xff555555);

        mTextPaint.getTextBounds(mText,0, mText.length(), mTextBound);
    }

    public static final String STATUS = "status";
    public static final String STATUS_ALPHA = "status_alpha";

    @Override
    protected Parcelable onSaveInstanceState() {

        Bundle bundle = new Bundle();
        bundle.putParcelable(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(STATUS));
            return;
        }

        super.onRestoreInstanceState(state);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int iconWith = Math.min(getMeasuredWidth()-getPaddingLeft()-getPaddingRight(),getMeasuredHeight()-getPaddingTop()-getPaddingBottom()-mTextBound.height());

        int left = getMeasuredWidth()/2 - iconWith/2;
        int top = (getMeasuredHeight()-mTextBound.height())/2-iconWith/2;
        mIconRect = new Rect(left, top, left+iconWith, top+iconWith);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mIconBitmap,null, mIconRect, null);
        int alpha = (int) Math.ceil(255*mAlpha);
        //在内存中准备可变色的mBitmap
        setTargetBitmap(alpha);

        //绘制原文本
        drawSourceText(canvas, alpha);
        //绘制变色的文本
        drawTargetText(canvas, alpha);

        canvas.drawBitmap(mBitmap, 0, 0, null);
    }

    private void drawTargetText(Canvas canvas, int alpha) {
        mTextPaint.setColor(mColor);
        mTextPaint.setAlpha(alpha);
        int x = getMeasuredWidth()/2-mTextBound.width()/2;
        int y = mIconRect.bottom+mTextBound.height();
        canvas.drawText(mText,x,y,mTextPaint);
    }

    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);
    }

    private void setTargetBitmap(int alpha) {
        mBitmap = Bitmap.createBitmap(getMeasuredWidth(),getMeasuredHeight(), Bitmap.Config.ARGB_8888);
        //以bitmap对象创建一个画布,则将内容都绘制在bitmap上,bitmap不得为null;
        mCanvas = new Canvas((mBitmap));
        mPaint = new Paint();
        mPaint.setColor(mColor);
        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);
    }

    public void setIconAlpha(float alpha)
    {
        this.mAlpha = alpha;
        //重绘
        if (Looper.getMainLooper() == Looper.myLooper()) //判断是不是主线程
        {
            invalidate();
        }else{
            postInvalidate();
        }
    }
}

activity代码

public class WeiXinActivity extends FragmentActivity implements View.OnClickListener, ViewPager.OnPageChangeListener {

    private ViewPager viewPager;
    private List<TabFragment> mTabs = new ArrayList<TabFragment>();
    private String[] mTitles = new String[]{
            "first fragemnt", "second fragment",
            "third fragment", "forth fragment"
    };
    private FragmentPagerAdapter mAdapter;

    private List<ChangeColorIconWithText> mTabIndicator = new ArrayList<ChangeColorIconWithText>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        overflowButtonController();
        getActionBar().setDisplayShowHomeEnabled(false);
        initViews();
        initDatas();
        viewPager.setAdapter(mAdapter);
        viewPager.setOnPageChangeListener(this);
    }


    private void initViews() {
        viewPager = (ViewPager) findViewById(R.id.viewPager);
        ChangeColorIconWithText one = (ChangeColorIconWithText) findViewById(R.id.indicator_one);
        ChangeColorIconWithText two = (ChangeColorIconWithText) findViewById(R.id.indicator_two);
        ChangeColorIconWithText three = (ChangeColorIconWithText) findViewById(R.id.indicator_three);
        ChangeColorIconWithText four = (ChangeColorIconWithText) findViewById(R.id.indicator_four);
        mTabIndicator.add(one);
        mTabIndicator.add(two);
        mTabIndicator.add(three);
        mTabIndicator.add(four);

        one.setOnClickListener(this);
        two.setOnClickListener(this);
        three.setOnClickListener(this);
        four.setOnClickListener(this);

        one.setIconAlpha(1.0f);
    }

    private void initDatas() {
        for (int i = 0; i < mTitles.length; i++)
        {
            TabFragment fragment = new TabFragment();
            Bundle bundle = new Bundle();
            bundle.putString(TabFragment.TITLE,mTitles[i]);
            fragment.setArguments(bundle);
            mTabs.add(fragment);
        }

        mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int i) {
                return mTabs.get(i);
            }

            @Override
            public int getCount() {
                return mTabs.size();
            }
        };

    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @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 (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

            }
        }
        return super.onMenuOpened(featureId, menu);
    }

    public void overflowButtonController()
    {
        ViewConfiguration config = ViewConfiguration.get(this);
        try {
            Field menuKey = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
            menuKey.setAccessible(true);
            menuKey.setBoolean(config, false);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onClick(View v) {
        resetOtherTabs();
        switch (v.getId())
        {
            case R.id.indicator_one:
                mTabIndicator.get(0).setIconAlpha(1.0f);
                viewPager.setCurrentItem(0,false);
                break;
            case R.id.indicator_two:
                mTabIndicator.get(1).setIconAlpha(1.0f);
                viewPager.setCurrentItem(1,false);
                break;
            case R.id.indicator_three:
                mTabIndicator.get(2).setIconAlpha(1.0f);
                viewPager.setCurrentItem(2,false);
                break;
            case R.id.indicator_four:
                mTabIndicator.get(3).setIconAlpha(1.0f);
                viewPager.setCurrentItem(3,false);
                break;
        }
    }

    private void resetOtherTabs() {
        for (int i = 0; i < mTabIndicator.size(); i++)
        {
            mTabIndicator.get(i).setIconAlpha(0);
        }
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (positionOffset > 0)
        {
            ChangeColorIconWithText left = mTabIndicator.get(position);
            ChangeColorIconWithText right = mTabIndicator.get(position+1);
            left.setIconAlpha(1-positionOffset);
            right.setIconAlpha(positionOffset);
        }
    }

    @Override
    public void onPageSelected(int i) {

    }

    @Override
    public void onPageScrollStateChanged(int i) {

    }
}

并且AndroidManifest.xml文件中application标签添加属性android:theme=”@style/AppBaseTheme”

<style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
        <item name="android:actionOverflowButtonStyle">@style/weixinActionOverflowButtonStyle</item>
    </style>
    <style name="weixinActionOverflowButtonStyle">
        <item name="android:src">@drawable/actionbar_add_icon</item>
    </style>

布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:chm="http://schemas.android.com/apk/res/com.chm.web"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
        android:orientation="vertical">
    <android.support.v4.view.ViewPager
            android:layout_width="match_parent" android:layout_height="0dp"
            android:layout_weight="1"
            android:id="@+id/viewPager">

    </android.support.v4.view.ViewPager>

    <LinearLayout android:layout_width="match_parent" android:layout_height="60dp"
            android:orientation="horizontal" android:background="@drawable/tab_bg">
        <com.chm.web.view.ChangeColorIconWithText
                android:layout_width="0dp" android:layout_height="fill_parent"
                android:layout_weight="1" chm:color="#ff45c01a"
                chm:text="@string/tab_weixin" chm:text_size="12sp" android:padding="6dp"
                chm:icon="@drawable/ic_menu_start_conversation" android:id="@+id/indicator_one"/>
        <com.chm.web.view.ChangeColorIconWithText
                android:layout_width="0dp" android:layout_height="fill_parent"
                android:layout_weight="1" chm:color="#ff45c01a"
                chm:text="@string/tab_contact" chm:text_size="12sp" android:padding="6dp"
                chm:icon="@drawable/ic_menu_friendslist" android:id="@+id/indicator_two"/>
        <com.chm.web.view.ChangeColorIconWithText
                android:layout_width="0dp" android:layout_height="fill_parent"
                android:layout_weight="1" chm:color="#ff45c01a"
                chm:text="@string/tab_discovery" chm:text_size="12sp" android:padding="6dp"
                chm:icon="@drawable/ic_menu_emoticons" android:id="@+id/indicator_three"/>
        <com.chm.web.view.ChangeColorIconWithText
                android:layout_width="0dp" android:layout_height="fill_parent"
                android:layout_weight="1" chm:color="#ff45c01a"
                chm:text="@string/tab_me" chm:text_size="12sp" android:padding="6dp"
                chm:icon="@drawable/ic_menu_allfriends" android:id="@+id/indicator_four"/>

    </LinearLayout>
</LinearLayout>

前面对于顶部导航栏的显示我们用了反射,其实这个不用反射也可以实现,个人觉得能不用反射就不用,毕竟反射是在没办法的情况下才使用。
使用ActionProvider就可以实现,下面看下Provider代码:

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class MyActionProvider extends ActionProvider {

    private Context context;
    /**
     * Creates a new instance. ActionProvider classes should always implement a
     * constructor that takes a single Context parameter for inflating from menu XML.
     *
     * @param context Context for accessing resources.
     */

    public MyActionProvider(Context context) {
        super(context);
        this.context = context;
    }

    @Override
    public View onCreateActionView() {
        return null;
    }

    @Override
    public void onPrepareSubMenu(SubMenu subMenu) {
        subMenu.clear();
        subMenu.add("发起群聊").setIcon(R.drawable.menu_group_chat_icon).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                Toast.makeText(context, "发起群聊", Toast.LENGTH_SHORT).show();
                return true;
            }
        });
        subMenu.add("添加朋友").setIcon(R.drawable.menu_add_icon).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                Toast.makeText(context, "添加朋友", Toast.LENGTH_SHORT).show();
                return true;
            }
        });
        subMenu.add("扫一扫").setIcon(R.drawable.men_scan_icon).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                Toast.makeText(context, "扫一扫", Toast.LENGTH_SHORT).show();
                return true;
            }
        });
        subMenu.add("意见反馈").setIcon(R.drawable.menu_feedback_icon).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                Toast.makeText(context, "意见反馈", Toast.LENGTH_SHORT).show();
                return true;
            }
        });

    }

    /**
     * 默认为false,不显示subMenu;
     * @return
     */
    @Override
    public boolean hasSubMenu() {
        return true;
    }
}

menu文件夹下的menu.xml文件

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/action_search"
          android:actionViewClass="android.widget.SearchView"
          android:icon="@drawable/actionbar_search_icon"
          android:showAsAction="ifRoom|collapseActionView"
          android:title="@string/search"/>

    <item
        android:id="@+id/action_plus"
        android:icon="@drawable/actionbar_add_icon"
        android:actionProviderClass="com.chm.web.MyActionProvider"
        android:showAsAction="always"
        android:title="@string/plus"
        />
    <!--<item-->
        <!--android:id="@+id/action_group_chat"-->
        <!--android:icon="@drawable/menu_group_chat_icon"-->
        <!--android:title="@string/action_group_chat"/>-->
    <!--<item-->
            <!--android:id="@+id/action_add_friend"-->
            <!--android:icon="@drawable/menu_add_icon"-->
            <!--android:title="@string/action_add_friend"/>-->
    <!--<item-->
            <!--android:id="@+id/action_scan"-->
            <!--android:icon="@drawable/men_scan_icon"-->
            <!--android:title="@string/action_scan"/>-->
    <!--<item-->
            <!--android:id="@+id/action_feedback"-->
            <!--android:icon="@drawable/menu_feedback_icon"-->
            <!--android:title="@string/action_feedback"/>-->

</menu>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值