仿微信底部栏图标切换时,颜色渐变效果

原创作者:AchillesL
若转载文章,请在明显的位置标明文章出处

1. 前言

  使用微信时,我们会发现:进行页面切换时,底部标题栏的图片会发生颜色深浅的变化。
  本文将介绍这种效果是如何实现的,最终效果如图1所示。

图1最终效果

2. 知识点

ArgbEvaluator.evaluate方法:

该方法用于根据一个起始颜色值和一个结束颜色值以及一个偏移量生成一个新的颜色。

DrawableCompat.setTintList方法:

通过该方法,可以动态地给图片进行着色处理。对于具体的用法,可以参考一下文章:http://www.jianshu.com/p/6bd7dd1cd491 本文不再讲述。

3. 准备工作

  我们创建一个Demo。Activity中包含一个ViewPager控件,并利用LinearLayout实现底部标题栏。
  布局代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.achillesl.bottombar.MainActivity">

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

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#eeeeee"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingBottom="10dp"
        android:paddingTop="10dp"
        >

        <ImageView
            android:id="@+id/ivOne"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            tools:src="@drawable/ic_action_alarm"/>

        <ImageView
            android:id="@+id/ivTwo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            tools:src="@drawable/ic_action_amazon"/>

        <ImageView
            android:id="@+id/ivThree"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            tools:src="@drawable/ic_action_anchor"/>

        <ImageView
            android:id="@+id/ivFour"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            tools:src="@drawable/ic_action_android"/>
    </LinearLayout>
</LinearLayout>

  主界面代码:

package com.achillesl.bottombar;

import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private ViewPager mViewPager;

    private List<TextView> mTipList = new ArrayList<>();
    //使用List存放ImageView对象
    private List<ImageView> mBottomImageViews = new ArrayList<>();
    //使用List存放ImageView对应的图片资源
    private List<Drawable> mBottomImageDrawables = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initTipList();
        initImageViewDrawable();

        // TODO: 2016/11/13 设置ViewPager的页面数
        mViewPager = (ViewPager) findViewById(R.id.viewPager);
        mViewPager.setAdapter(new PagerAdapter() {

            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                container.removeView(mTipList.get(position));
            }

            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                TextView textView = mTipList.get(position);
                container.addView(textView);
                return textView;
            }

            @Override
            public int getCount() {
                return 4;
            }

            @Override
            public boolean isViewFromObject(View view, Object object) {
                return view == object;
            }
        });
    }

    private void initImageViewDrawable() {
        mBottomImageViews.add((ImageView) findViewById(R.id.ivOne));
        mBottomImageViews.add((ImageView) findViewById(R.id.ivTwo));
        mBottomImageViews.add((ImageView) findViewById(R.id.ivThree));
        mBottomImageViews.add((ImageView) findViewById(R.id.ivFour));

        mBottomImageDrawables.add(ContextCompat.getDrawable(this,R.drawable.ic_action_alarm).mutate());
        mBottomImageDrawables.add(ContextCompat.getDrawable(this,R.drawable.ic_action_amazon).mutate());
        mBottomImageDrawables.add(ContextCompat.getDrawable(this,R.drawable.ic_action_anchor).mutate());
        mBottomImageDrawables.add(ContextCompat.getDrawable(this,R.drawable.ic_action_android).mutate());
    }

    private void initTipList() {
        ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams
                .MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        for (int i = 0; i < 4; i++) {
            TextView textView = new TextView(this);
            textView.setText("第" + (i + 1) + "个页面");
            textView.setLayoutParams(layoutParams);
            textView.setGravity(Gravity.CENTER);
            mTipList.add(textView);
        }
    }
}

  上面代码中,我们使用List统一管理图片资源、ImageView实例。

4. 颜色渐变

4.1 设置图标初始颜色

  这一步,我们将设置底部标题栏图标的颜色。选择的颜色为蓝色,未选中的蓝色为灰色。

4.1.1 定义并初始化颜色

在color.xml文件中定义颜色

<color name="buttonSelect">#3F51B5</color>
<color name="buttonUnSelect">#a4a3a3</color>

4.1.2 在Activity中添加变量

private int mBottomColorSelect;
private int mBottomColorUnSelect;

4.1.3 设置不同图标的颜色

  添加setImageViewSelect方法,代码如下:

private void setImageViewSelect(int selectIndex) {
    for (int index = 0; index < mBottomImageViews.size(); index++) {
        ImageView imageView = mBottomImageViews.get(index);
        Drawable drawable = mBottomImageDrawables.get(index);
        if (index == selectIndex) {
            imageView.setImageDrawable(tintDrawable(drawable, ColorStateList.valueOf
                    (mBottomColorSelect)));
        } else {
            imageView.setImageDrawable(tintDrawable(drawable, ColorStateList.valueOf
                    (mBottomColorUnSelect)));
        }
    }
}

4.1.4 修改initImageViewDrawable方法

private void initImageViewDrawable() {
    /*Add begin*/
    mBottomColorSelect = ContextCompat.getColor(this, R.color.buttonSelect);
    mBottomColorUnSelect = ContextCompat.getColor(this, R.color.buttonUnSelect);
    /*Add end*/

    mBottomImageViews.add((ImageView) findViewById(R.id.ivOne));
    ...
    mBottomImageDrawables.add(ContextCompat.getDrawable(this,R.drawable.ic_action_android).mutate());

    /*Add begin*/
    setImageViewSelect(0);
    /*Add end*/
}

  此时,完成了底部栏图标的初始化,如图2所示。

图 2

4.2 设置ViewPager滑动监听

  这时,我们需要给ViewPager添加滑动监听事件,并且可以联想到:颜色的渐变,应该与ViewPager的滑动距离有关。
  因此,在监听器的onPageScrolled方法中添加changeImageViewDrawable方法,根据ViewPager的滑动偏移量,动态改变图标的颜色。

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            changeImageViewDrawable(position,positionOffset);
        }

        @Override
        public void onPageSelected(int position) {

        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });

4.2.1 编写changeImageViewDrawable方法

  这一步主要用到ArgbEvaluator类的evaluate方法,该方法的用法很简单:

public Object evaluate(float fraction, Object startValue, Object endValue)

  参数fraction:
  指起始颜色到目标颜色的偏移值。该值我们用ViewPager的页面偏移值positionOffset传入。

  参数startValue:起始颜色

  参数endValue:目标颜色

  changeImageViewDrawable方法的代码:

private void changeImageViewDrawable(int position, float positionOffset) {
    ImageView ivFrom = null;
    ImageView ivTo = null;

    Drawable drawableFrom = null;
    Drawable drawableTo = null;

    ivFrom = mBottomImageViews.get(position);
    drawableFrom = mBottomImageDrawables.get(position);

    if (position != mBottomImageDrawables.size() - 1) {
        ivTo = mBottomImageViews.get(position + 1);
        drawableTo = mBottomImageDrawables.get(position + 1);
    } else {
        ivTo = null;
        drawableTo = null;
    }

    if (ivFrom != null) {
        int colorStart = (int) mArgbEvaluator.evaluate(positionOffset, mBottomColorSelect,
                mBottomColorUnSelect);
        Drawable drawableColorStart = tintDrawable(drawableFrom, ColorStateList.valueOf
                (colorStart));
        ivFrom.setImageDrawable(drawableColorStart);
    }
    if (ivTo != null) {
        int colorStart = (int) mArgbEvaluator.evaluate(positionOffset, mBottomColorUnSelect,
                mBottomColorSelect);
        Drawable drawableColorEnd = tintDrawable(drawableTo, ColorStateList.valueOf
                (colorStart));
        ivTo.setImageDrawable(drawableColorEnd);
    }
}

3 小问题优化

  实现具体业务时,往往点击底部标题栏的图片时,需要切换到具体的页面。细心的同学可能会发现,若起始按钮与目标按钮相隔较远时,其他按钮图片上可能遗留“残影”。
  解决这个问题很简单,只要在ViewPager页面切换完成时,再一次延时设置底部图标的颜色就可以了。

@Override
public void onPageSelected(final int position) {
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            setImageViewSelect(position);
        }
    },DELAY_TIME);
}

全部代码

package com.achillesl.bottombar;

import android.animation.ArgbEvaluator;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private ViewPager mViewPager;

    private List<TextView> mTipList = new ArrayList<>();
    //使用List存放ImageView对象
    private List<ImageView> mBottomImageViews = new ArrayList<>();
    //使用List存放ImageView对应的图片资源
    private List<Drawable> mBottomImageDrawables = new ArrayList<>();

    private ArgbEvaluator mArgbEvaluator = new ArgbEvaluator();
    private int mBottomColorSelect;
    private int mBottomColorUnSelect;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initTipList();
        initImageViewDrawable();

        // TODO: 2016/11/13 设置ViewPager的页面数
        mViewPager = (ViewPager) findViewById(R.id.viewPager);
        mViewPager.setAdapter(new PagerAdapter() {

            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                container.removeView(mTipList.get(position));
            }

            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                TextView textView = mTipList.get(position);
                container.addView(textView);
                return textView;
            }

            @Override
            public int getCount() {
                return 4;
            }

            @Override
            public boolean isViewFromObject(View view, Object object) {
                return view == object;
            }
        });
        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            private final int DELAY_TIME = 100;
            private Handler handler = new Handler();

            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                changeImageViewDrawable(position,positionOffset);
            }

            @Override
            public void onPageSelected(final int position) {
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        setImageViewSelect(position);
                    }
                },DELAY_TIME);
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
    }

    private void changeImageViewDrawable(int position, float positionOffset) {
        ImageView ivFrom = null;
        ImageView ivTo = null;

        Drawable drawableFrom = null;
        Drawable drawableTo = null;

        ivFrom = mBottomImageViews.get(position);
        drawableFrom = mBottomImageDrawables.get(position);

        if (position != mBottomImageDrawables.size() - 1) {
            ivTo = mBottomImageViews.get(position + 1);
            drawableTo = mBottomImageDrawables.get(position + 1);
        } else {
            ivTo = null;
            drawableTo = null;
        }

        if (ivFrom != null) {
            int colorStart = (int) mArgbEvaluator.evaluate(positionOffset, mBottomColorSelect,
                    mBottomColorUnSelect);
            Drawable drawableColorStart = tintDrawable(drawableFrom, ColorStateList.valueOf
                    (colorStart));
            ivFrom.setImageDrawable(drawableColorStart);
        }
        if (ivTo != null) {
            int colorStart = (int) mArgbEvaluator.evaluate(positionOffset, mBottomColorUnSelect,
                    mBottomColorSelect);
            Drawable drawableColorEnd = tintDrawable(drawableTo, ColorStateList.valueOf
                    (colorStart));
            ivTo.setImageDrawable(drawableColorEnd);
        }
    }

    private void initImageViewDrawable() {
        /*Add begin*/
        mBottomColorSelect = ContextCompat.getColor(this, R.color.buttonSelect);
        mBottomColorUnSelect = ContextCompat.getColor(this, R.color.buttonUnSelect);
        /*Add end*/

        mBottomImageViews.add((ImageView) findViewById(R.id.ivOne));
        mBottomImageViews.add((ImageView) findViewById(R.id.ivTwo));
        mBottomImageViews.add((ImageView) findViewById(R.id.ivThree));
        mBottomImageViews.add((ImageView) findViewById(R.id.ivFour));

        mBottomImageDrawables.add(ContextCompat.getDrawable(this,R.drawable.ic_action_alarm).mutate());
        mBottomImageDrawables.add(ContextCompat.getDrawable(this,R.drawable.ic_action_amazon).mutate());
        mBottomImageDrawables.add(ContextCompat.getDrawable(this,R.drawable.ic_action_anchor).mutate());
        mBottomImageDrawables.add(ContextCompat.getDrawable(this,R.drawable.ic_action_android).mutate());

        for (int i = 0; i < mBottomImageViews.size(); i++) {
            ImageView imageView = mBottomImageViews.get(i);
            final int index = i;
            imageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    mViewPager.setCurrentItem(index);
                }
            });
        }

        /*Add begin*/
        setImageViewSelect(0);
        /*Add end*/
    }

    private void setImageViewSelect(int selectIndex) {
        for (int index = 0; index < mBottomImageViews.size(); index++) {
            ImageView imageView = mBottomImageViews.get(index);
            Drawable drawable = mBottomImageDrawables.get(index);
            if (index == selectIndex) {
                imageView.setImageDrawable(tintDrawable(drawable, ColorStateList.valueOf
                        (mBottomColorSelect)));
            } else {
                imageView.setImageDrawable(tintDrawable(drawable, ColorStateList.valueOf
                        (mBottomColorUnSelect)));
            }
        }
    }

    public Drawable tintDrawable(Drawable drawable, ColorStateList colors) {
        final Drawable wrappedDrawable = DrawableCompat.wrap(drawable);
        DrawableCompat.setTintList(wrappedDrawable, colors);
        return wrappedDrawable;
    }

    private void initTipList() {
        ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams
                .MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        for (int i = 0; i < 4; i++) {
            TextView textView = new TextView(this);
            textView.setText("第" + (i + 1) + "个页面");
            textView.setLayoutParams(layoutParams);
            textView.setGravity(Gravity.CENTER);
            mTipList.add(textView);
        }
    }
}

代码地址

  https://github.com/AchillesLzg/jianshu-weixinbottombar

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,042评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,089评论 4 62
  • 最近一直处于加班的状态,周末一个下午也只画了一张,一幅画一定要一气呵成才行,中间停顿了后面就无从下手了!
    小妖耳子阅读 347评论 2 2
  • 有一种亲情叫做过年 文/小编 YANN 独在异乡为异客,每逢佳节倍思亲。王维 长期漂泊在外的异乡人,每逢节气到来或...
    ISALPARIS阅读 2,287评论 0 2
  • 众所周知,在线教育打破了名校名师的空间和时间壁垒,是教育传播更加广泛。然而实际的现状表明,在线教育不仅没有缩小差距...
    因酷时代阅读 247评论 0 0