微信底部滑动时图标渐变色的实现

周末想实现一下微信底部的渐变图案,折腾了一波,效果总算得上是差强人意。

下面是QQ的微信图标

首先想到两个方案就是设置背景透明度,和属性动画。但效果都被否决了,属性动画效果逼真一些,但是顶多算是B货。要实现高仿的A货,尽管不喜欢用还是得自定义View了。

动画折腾了好久尽管效果很接近,但是效果还是有所区别。但是自从看了微信的图片资源后,立马就反应过来了,原来这玩意是这样设计的。

利用两张同样大小的图片,一张是透明的,一张是填充色,这个效果就是把两张图片重叠起来,然后不断改变填充色的alpha透明度就实现了。原本以为很高深的技术,其实就是用的两张图片重叠混合而成的。原理知道了,接下来就是代码实现了,标准的自定义view流程。

首先在values文件夹下面新建attrs目录,接着就是自定义需要用到的属性了。

<resources>

    <declare-styleable name="MyView">
        <attr name="bottomBitmap format="reference" />
        <attr name="topBitmap" format="reference" />
        <attr name="text" format="string" />
        <attr name="textsize" format="dimension" />
    </declare-styleable>
</resources>

以上代码分别定义了,底部图片,上部图片,文字,和文字大小属性

接下来就是自定义View的实现,新建一个class类MyView继承View。

 public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //从xml定义属性中拿到对应数值
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyView);
        int indexCount = ta.getIndexCount();
        for (int i = 0; i < indexCount; i++) {
            int index = ta.getIndex(i);
            switch (index) {
                case R.styleable.MyView_text:
                    mText = ta.getString(index);
                    break;
                case R.styleable.MyView_textsize:
                    mTextSize = ta.getDimension(index, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.MyView_bottomBitmap:
                    BitmapDrawable bottomDrawable = (BitmapDrawable) ta.getDrawable(index);
                    mBottomBitmap = bottomDrawable.getBitmap().copy(Bitmap.Config.ARGB_8888, true);
                    break;
                case R.styleable.MyView_topBitmap:
                    BitmapDrawable topDrawable = (BitmapDrawable) ta.getDrawable(index);
                    mTopBitmap = topDrawable.getBitmap();
                    break;
            }
        }
        //拿到底图的宽高
        mBitmapWidth = mBottomBitmap.getWidth();
        mBitmapHeight = mBottomBitmap.getHeight();
        //初始化字体画笔
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setTextSize(mTextSize);
        mTextBounds = new Rect();
        mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBounds);
        mTextPaint.setTextSize(mTextSize);
        //初始化底图和覆盖图画笔
        mBottomBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTopBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }

以上为MyView的构造方法,其中利用TypedArray拿到自定义的属性值,并且通过switch语句分别赋值给成员变量。之后是拿到底图的宽高,然后分别初始化字体画笔,底图和覆盖图画笔。

接下来就是view的测量:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //拿到测量宽高
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        //初始化bitmap的绘制区域
        mBitmapRect = new Rect(width / 2 - mBitmapWidth / 2, height / 2 - mBitmapHeight / 2 - mTextBounds.height() / 2,
                width / 2 + mBitmapHeight / 2, height / 2 + mBitmapHeight / 2 - mTextBounds.height() / 2);
        xText = width / 2-mTextBounds.width()/2 ;
        yText = mBitmapRect.bottom + mTextBounds.height();
    }

在view的测量中,首先拿到view的宽高,然后初始化mBitmapRect给底图设置绘制区域,这里是为了绘制的底图在view的中间显示。然后xText,yText分别是字体绘制的开始位置和绘制基线位置,这里只要是让字体正中显示在底图下面。

之后是view的绘制:

 @Override
    protected void onDraw(Canvas canvas) {
        drawBitmap(canvas);
        drawText(canvas);
    }
    private void drawText(Canvas canvas) {
        mTextPaint.setColor(0x3c3c3c);
        mTextPaint.setAlpha(255 - mAlpha);
        canvas.drawText(mText, xText, yText, mTextPaint);
        mTextPaint.setColor(0x00FF00);
        mTextPaint.setAlpha(mAlpha);
        canvas.drawText(mText, xText, yText, mTextPaint);

    }
    private void drawBitmap(Canvas canvas) {
        //初始化画布
        mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mBitmap);
        //画底图
        mBottomBitmapPaint.setAlpha(255 - mAlpha);
        mCanvas.drawBitmap(mBottomBitmap, null, mBitmapRect, mBottomBitmapPaint);
        //画覆盖图
        mTopBitmapPaint.setAlpha(mAlpha);
        mCanvas.drawBitmap(mTopBitmap, null, mBitmapRect, mTopBitmapPaint);
        LogUtils.E("alpha==" + mAlpha);
        //将底图和覆盖图的综合效果绘制出来
        canvas.drawBitmap(mBitmap, 0, 0, null);
    }

这里分两部分一部分是drawBitmap,另一部分是drawText。绘制bitmap方法里,首先初始化一张新的bitmap画布,然后绘制底图(带mAlpha值,范围为的0—255),之后绘制覆盖图(带mAlpha值,范围为的0—255)。最后调用canvas.drawBitmap把两张图的综合效果绘制出来。绘制问题方法里,很简单,文字绘制两遍,先绘制灰色,再绘制绿色,利用 mTextPaint.setAlpha()。显示最终文字的综合效果。

最后就是给MyView设置一个setAlpha()方法

  public void setAlpha(float alpha) {
        int ceil = (int) Math.ceil(255 * alpha);
        mAlpha = ceil;
        invalidate();
    }

ceil是向上取整,设置完Alpha值后调用invalidate()重绘。这时候MyView就全部完成了。

接下来就是主界面的布局了,微信中间部分采用viewpager+fragment的方式实现。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ethan="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <RelativeLayout
        android:id="@+id/rl_title"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="@mipmap/background"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginLeft="10dp"
            android:gravity="center"
            android:text="微信"
            android:textColor="#ffffff"
            android:textSize="24sp" />
        <Button
            android:id="@+id/button_search"
            android:layout_width="60dp"
            android:layout_height="50dp"
            android:layout_alignParentRight="true"
            android:layout_centerInParent="true"
            android:layout_marginRight="80dp"
            android:background="@mipmap/search" />

        <Button
            android:id="@+id/button_more"
            android:layout_width="60dp"
            android:layout_height="50dp"
            android:layout_alignParentRight="true"
            android:layout_centerInParent="true"
            android:layout_marginRight="10dp"
            android:background="@mipmap/more" />
    </RelativeLayout>

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

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

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:background="#33000000" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:orientation="horizontal">

        <com.example.administrator.weichart.activity.view.MyView
            android:id="@+id/weixin"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            ethan:bottomBitmap="@mipmap/weixin"
            ethan:text="微信"
            ethan:textsize="16sp"
            ethan:topBitmap="@mipmap/weixin_green" />

        <com.example.administrator.weichart.activity.view.MyView
            android:id="@+id/tongxunlu"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            ethan:bottomBitmap="@mipmap/tongxunlu"
            ethan:text="通讯录"
            ethan:textsize="16sp"
            ethan:topBitmap="@mipmap/tongxunlu_green" />

        <com.example.administrator.weichart.activity.view.MyView
            android:id="@+id/faxian"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            ethan:bottomBitmap="@mipmap/faxian"
            ethan:text="发现"
            ethan:textsize="16sp"
            ethan:topBitmap="@mipmap/faxian_green" />

        <com.example.administrator.weichart.activity.view.MyView
            android:id="@+id/wo"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            ethan:bottomBitmap="@mipmap/wo"
            ethan:text="我"
            ethan:textsize="16sp"
            ethan:topBitmap="@mipmap/wo_green" />
    </LinearLayout>

</LinearLayout>

主布局有了,还需要四个Fragment,以下是fragment_weixin.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="this is weixin fragment"
        android:textColor="#0000ff"
        android:textSize="18sp" />

</LinearLayout>

简单起见,Fragment里面就放置了一个TextView。

最后就是主界面HomeActivity了


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.home_layout);
        initViews();
        initData();
        initEvent();
        mViewPager.setAdapter(new myViewPagerAdapter(getSupportFragmentManager()));
        mViewPager.addOnPageChangeListener(new MyPageChangeListener());

    }

在HomeActivity的onCreate方法中,分别初始化View, 初始化Data,初始化Event,之后给mViewPager设置Adapter,并且设置页面变化监听。

 private void initEvent() {
        weiXinMyView.setAlpha(1f);//设置第一个第一个视图的alpha为1
    }

    /**
     * 初始化数据
     */
    private void initData() {
        mFragmentList = new ArrayList<Fragment>();
        mFragmentList.add(new WeiXInFragment());
        mFragmentList.add(new TongXunLuFragment());
        mFragmentList.add(new FaXianFragment());
        mFragmentList.add(new WoFragment());
        mMyViewList = new ArrayList<MyView>();
        mMyViewList.add(weiXinMyView);
        mMyViewList.add(tongXunLuMyView);
        mMyViewList.add(faXianMyView);
        mMyViewList.add(woMyView);

    }

    /**
     * 初始化view
     */
    private void initViews() {
        //初始化底部四个view
        weiXinMyView = (MyView) findViewById(R.id.weixin);
        tongXunLuMyView = (MyView) findViewById(R.id.tongxunlu);
        faXianMyView = (MyView) findViewById(R.id.faxian);
        woMyView = (MyView) findViewById(R.id.wo);
        //给底部四个view设置点击事件
        weiXinMyView.setOnClickListener(this);
        tongXunLuMyView.setOnClickListener(this);
        faXianMyView.setOnClickListener(this);
        woMyView.setOnClickListener(this);
        //初始化底部四个viewpager
        mViewPager = (ViewPager) findViewById(R.id.viewpager);
        }

以上为数据和view的初始化。接下是底部四个按钮的点击事件,点击当前按钮会回到这个界面。


 /**
     * 四个按钮的点击事件
     * @param v
     */
    @Override
    public void onClick(View v) {
       switch (v.getId())
       {
           case R.id.weixin:
               mViewPager.setCurrentItem(0);
               break;
           case R.id.tongxunlu:
               mViewPager.setCurrentItem(1);
               break;
           case R.id.faxian:
               mViewPager.setCurrentItem(2);
               break;
           case R.id.wo:
               mViewPager.setCurrentItem(3);
               break;
       }

    }

最后就是viewpager的adapter和viewpager 的页面滑动监听事件了。

 private class MyPageChangeListener implements ViewPager.OnPageChangeListener {

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            if (positionOffset > 0) {
                MyView leftView = mMyViewList.get(position);
                leftView.setAlpha(1 - positionOffset);
                LogUtils.E("positionOffset==" + (1 - positionOffset));
                MyView rightView = mMyViewList.get(position + 1);
                rightView.setAlpha(positionOffset);
                LogUtils.E("position==" + position);
            }
            if(position>mFragmentList.size())
            {
                mViewPager.setCurrentItem(1);
            }

        }
        @Override
        public void onPageSelected(int position) {

        }
        @Override
        public void onPageScrollStateChanged(int state) {

        }
    }
    private class myViewPagerAdapter extends FragmentStatePagerAdapter {

        public myViewPagerAdapter(FragmentManager fm) {
            super(fm);
        }
        @Override
        public Fragment getItem(int position) {
            return mFragmentList.get(position);
        }
        @Override
        public int getCount() {
            return mFragmentList.size();
        }
    }

在监听的页面活动的过程中,首先拿到左右两个View的对象,接着根据滑动的距离来确定alpha值,导致view的重绘。

到这里整个微信底部栏就实现了。效果和原版一模一样。

源码下载:

仿微信底部实现

PS:在看微信图片集的时候,还发现了一个有意思的东西,那就是色子。

分析可知原来摇色子的动画其实就是几张模糊的图片来回的在切换。这也就解释了为什么摇色子的时候,转发的色子一直就是那个点数。知道了这个以后摇色子抢红包就不用害怕了,想要几点直接用转发几点的色子就结了,想摇几点就是几点。

时间: 2024-10-12 14:35:10

微信底部滑动时图标渐变色的实现的相关文章

微信底部导航渐变效果-----viewpager&amp;PorterDuffXfermode

实现这个功能主要涉及的知识点有 ViewPager PorterDuffXfermode 自定义视图 ViewPager 关于ViewPager需要注意的知识主要是OnPageChangeListener,该接口的三个方法如下 public abstract void onPageScrollStateChanged (int state) public abstract void onPageScrolled (int position, float positionOffset, int p

Android仿微信底部菜单栏+顶部菜单栏(附源码)

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 本文要实现仿微信微信底部菜单栏+顶部菜单栏,采用ViewPage来做,每一个page对应一个XML,当手指在ViewPage左右滑动时,就相应显示不同的page(其实就是xml)并且同时改变底部菜单按钮的图片变暗或变亮,同时如果点击底部菜单按钮,左右滑动page(其实就是xml)并且改变相应按钮的亮度. 最终效果:源码免费下载 一.布局 1.顶部菜单布局,命名为top_layout.xml

android高仿微信底部渐变导航栏

最近有很多人微信底部的变色卡片导航是怎么做的,我在网上看了好几个例子,都是效果接近,都存有一些差异,自己琢磨也做了一个,几乎99%的还原,效果还不错吧 仔细观察微信图片,发现他有两部分内容,外面的边框和里面的内容,内容的颜色由绿变为透明,这部分可以直接改变透明度,外面的边框,颜色在灰色和绿色之间变化,就不能简单的改变透明度了,ImageView的tint 为我们提供了可行方案,tint可以为图标着色,既可以在xml中,也可以在代码中设置,一共有16中模式,分别为 在xml中设置:直接添加tint

iOS navigationBar和tabBar变透明 &amp; navigationBar根据滑动距离的渐变色实现

navigationBar变为纯透明 //第一种方法 //导航栏纯透明 [self.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; //去掉导航栏底部的黑线 self.navigationBar.shadowImage = [UIImage new]; //第二种方法 [[self.navigationBar subviews] objectAtIndex:0].alpha =

html css 仿微信底部自定义菜单

最近几个月一直从事微信开发,从刚开始的懵懂渐渐成长了一点.今天觉得微信底部自定义菜单,如果能在html的页面上也能显示就好了. 记得以前看过某个网页有类似效果.查找了该网页的css.  ok现在html css 实现微信自定义菜单效果. 不多说直接上代码. /** * 仿微信自定义菜单 * * @author xuyw * @email [email protected] * @date 2014-07-27 */ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HT

怎样实现分享网站文章到微信朋友圈时指定缩略图

当下朋友圈很火,很多企业都将微信朋友圈作为品牌传播的途径,经常会发一些精彩的文章到微信朋友圈供大家自发传播,这样的想法很好,对于优质的内容,网友们也乐于转发与朋友们分享,对品牌宣传与推广确实是有好处的. 通过微信公众平台发文章,需要登录微信公众平台,非常麻烦,而且不能同步到网站中,同一篇文章可能要在多在地方编辑发布,增加了人力和时间成本,有没有一劳永逸的办法呢?当然是有的,其中之一是做一个能在台式机.笔记本电脑.平板电脑.手机上显示出一致效果的网站(IT领域叫响应式网站或全屏幕适应网站),先在这

微信音乐回复时出现“链接无效,无法播放”的情况

我用的是新浪云计算提供的云服务器,将wx_example.php上传到代码区后进行测试. 服务器的存储空间是新浪云界面下的 服务管理 -> Storage 下的domain. 在进行回复音乐信息的测试时发现,上传在云端的MP3不能被访问.可以用浏览器打开上传音乐的MP3外链,但是在微信端回复时,就会出现"链接无效,无法播放"的情况. 原因在于当你在新浪云上传文件后,会默认启用防盗链,除了指定的网址可以访问外,其它的访问都会被屏蔽,所以导致在微信端不能访问该文件. 解决方法有以下两

gallery左右滑动时图片淡入淡出

前几天,公司项目有一个功能要做成滑动图片的淡入淡出,要一边滑动一边改变,所以ViewFlipper左右滑动效果就不能了.网上找了很久,也找不到资料,所以自己写了一个,通过滑动改变imageView的透明度.当按下图片时,先记下imageView的位置,图片滑动时,位置发生变化,就可以算出移动的距离,从而可以算出alpha的值.当图片向左滑动时,设置imageView的Alpha即imageView.setAlpha(255-alpha),设置下一个nextView的Alpha即nextView.

html css 仿微信底部自己定义菜单

近期几个月一直从事微信开发,从刚開始的懵懂渐渐成长了一点. 今天认为微信底部自己定义菜单,假设能在html的页面上也能显示就好了. 记得曾经看过某个网页有类似效果.查找了该网页的css.  ok如今html css 实现微信自己定义菜单效果. 不多说直接上代码. /** * 仿微信自己定义菜单 * * @author xuyw * @email [email protected] * @date 2014-07-27 */ <!DOCTYPE HTML PUBLIC "-//W3C//DT