您的位置:首页 > 移动开发 > 微信开发

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

2016-06-14 00:06 573 查看
最近有很多人问微信底部的变色卡片导航是怎么做的,我在网上看了好几个例子,都是效果接近,都存有一些差异,自己琢磨也做了一个,几乎99%的还原,效果还不错吧



仔细观察微信图片,发现他有两部分内容,外面的边框和里面的内容,内容的颜色由绿变为透明,这部分可以直接改变图片透明度,外面的边框,颜色在灰色和绿色之间变化,就不能简单的改变透明度了,ImageView的tint 为我们提供了可行方案,tint可以为图标着色,既可以在xml中,也可以在代码中设置,一共有16种颜色混合模式,分别为



按照我的理解简单解释下这16种模式,有两张图片,一张dst 目标图标,可以看做底部的背景,一张 src图片,可以看做上面的内容,clear:在底部中清除内容所在的区域,srcover:内容覆盖在背景之上,DstOver:背景覆盖在内容之上,SrcIn:显示src和dst相交的src区域,dstin:显示相交的dst区域,SrcOut:显示不相交的src区域,DstOut:显示不相交的Dst区域,SrcAtTop:显示dst所在的区域,相交区域显示Src,DstAtTop:显示src所在区域,相交区域显示Dst,Xor:异或,相交区域不显示,其他区域保留,darken,src覆盖在在dst之上,相交区域src以最暗显示,lighten:src覆盖在dst之上,相交区域src以最亮显示,Multiply:显示相交区域的混合,Screen:显示全部区域,相交区域以全色显示,即白色

在xml中设置:直接添加tint属性,选择tintMode模式

<ImageView
android:id="@+id/green"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@mipmap/green"
android:tint="@color/colorPrimary"
android:tintMode="src_in"/>


在java代码中设置

mGreenImageView.setColorFilter(color,mode) mode参数类型  PorterDuff.Mode

为了理解不同颜色,不同透明度的图片在设置不同tint模式、不同的颜色后的变化,写了一个demo,四个滑动条分别代表了颜色的alpha,R、G、B值,改变滑动位置,通过Color.argb(alpha,red,green,blue)动态组合出不同的颜色,通过Spinner选择不同的模式,给图像设置不同的模式和不同的tint颜色,展示不同的效果,观察结果,可以看到给imageview设置tint后,图标最后展示出来的颜色不仅和设置的模式相关,还和图像的原有颜色和透明度相关。具体是怎样的相关性,语言描述不清,请自行体会。




选了实心绿,透明白,实心紫红的三张图片进行测试验证







界面很简单,只是一些基本的控件,代码如下

public class SimpleActivity extends Activity {
private ImageView mGreenImageView;
private ImageView mTransparentImageView;
private ImageView mRedImageView;
//透明度滑动条
private SeekBar mTransparentSeekBar;
private Spinner mSpinner;
//红色滑动条
private SeekBar mRedSeekBar;
//绿色滑动条
private SeekBar mGreenSeekBar;
//蓝色滑动条
private SeekBar mBlueSeekBar;
private TextView mTextView;
private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener; //滑动条监听器
//PorterDuff.Mode 列表
private static final PorterDuff.Mode[] MODES = new PorterDuff.Mode[]{
PorterDuff.Mode.ADD,
PorterDuff.Mode.CLEAR,
PorterDuff.Mode.DARKEN,
PorterDuff.Mode.DST,
PorterDuff.Mode.DST_ATOP,
PorterDuff.Mode.DST_IN,
PorterDuff.Mode.DST_OUT,
PorterDuff.Mode.DST_OVER,
PorterDuff.Mode.LIGHTEN,
PorterDuff.Mode.MULTIPLY,
PorterDuff.Mode.OVERLAY,
PorterDuff.Mode.SCREEN,
PorterDuff.Mode.SRC,
PorterDuff.Mode.SRC_ATOP,
PorterDuff.Mode.SRC_IN,
PorterDuff.Mode.SRC_OUT,
PorterDuff.Mode.SRC_OVER,
PorterDuff.Mode.XOR
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
mGreenImageView = (ImageView) findViewById(R.id.green);
mTransparentImageView= (ImageView) findViewById(R.id.transparent);
mRedImageView= (ImageView) findViewById(R.id.red);
mTextView= (TextView) findViewById(R.id.text);
mTransparentSeekBar = (SeekBar) findViewById(R.id.alpha_seekbar);
mRedSeekBar= (SeekBar) findViewById(R.id.red_seekbar);
mGreenSeekBar= (SeekBar) findViewById(R.id.green_seekbar);
mBlueSeekBar= (SeekBar) findViewById(R.id.blue_seekbar);
mSpinner= (Spinner) findViewById(R.id.spinner);
SpinnerAdapter spinnerAdapter=ArrayAdapter.createFromResource(SimpleActivity.this,R.array.blend_modes, android.R.layout.simple_list_item_1);
mSpinner.setAdapter(spinnerAdapter);
initListener();

}

private void initListener() {
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
updateImage(getRGBColor(),getMode());
}

@Override
public void onNothingSelected(AdapterView<?> parent) {

}
});
mOnSeekBarChangeListener=new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
updateImage(getRGBColor(),getMode());
}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {

}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {

}
};
mTransparentSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
mRedSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
mGreenSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
mBlueSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
}

private PorterDuff.Mode getMode() {
return MODES[mSpinner.getSelectedItemPosition()];
}

/**
* @return 根据ARGB颜色滑动条的数值计算颜色值
*/
private int getRGBColor() {
int alpha= mTransparentSeekBar.getProgress();
int red=mRedSeekBar.getProgress();
int green=mGreenSeekBar.getProgress();
int blue=mBlueSeekBar.getProgress();
return Color.argb(alpha,red,green,blue);
}

/**
* 更新颜色 模式
* @param color
* @param mode
*/
private void updateImage(int color,  PorterDuff.Mode mode ) {
mGreenImageView.setColorFilter(color,mode);
mTransparentImageView.setColorFilter(color,mode);
mRedImageView.setColorFilter(color,mode);
mTextView.setTextColor(color);
}
}
通过这个例子,可以看到在src_in模式下,如果给图标设置了tint,即着色后,图片显现的颜色和原有颜色无关,只和图像原有的透明度有关,图像越透明,着色越淡,图像实,着色越深



现在已经介绍了ImageView中tint使用的基础知识,下面我们就开始我们标题中所说的微信导航栏效果,我用肉眼能识别精度仔细观察了微信,发现微信主要有两个部分,外面的框和里面的内容,从上一页滑到下一页过程中,分为两部分,滑动到一半前,上一页的外框保持不变,内容由绿色变为透明,下一页的外框由灰色变为绿色,滑到一半后,上一页的外框由绿色变为灰色,下一页的内容由透明变为绿色,现象已经描述的很清楚了,现在我们用两张图片实现效果,一张边框图片,一张内容图片,为了方便,内容只改变透明度,选为绿色,前面的实验知道使用tint
的srcin模式,可以把任意颜色的图片渲染出喜欢的颜色,边框可以为任意颜色,监听ViewPager滑动状态,根据ViewPager偏移量,计算当前颜色值,设置tint着色渲染

布局文件

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context="com.why.drawabletinttest.PagerActivity">
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:id="@+id/pager_view"
android:layout_height="match_parent"></android.support.v4.view.ViewPager>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/image1_white"/>
<ImageView
android:id="@+id/image1_top"
android:layout_width="40dp"
android:layout_height="40dp"
android:tint="@android:color/transparent"
android:layout_gravity="center_horizontal"
android:src="@mipmap/chats_green"/>
<ImageView
android:id="@+id/image1"
android:layout_width="40dp"
android:layout_height="40dp"
android:tint="@android:color/transparent"
android:layout_gravity="center_horizontal"
android:src="@mipmap/chats"/>
</FrameLayout>
<TextView
android:id="@+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="晴川"/>
</LinearLayout>

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/image2_white"/>
<ImageView
android:id="@+id/image2_top"
android:layout_width="40dp"
android:layout_height="40dp"
android:tint="@android:color/transparent"
android:layout_gravity="center_horizontal"
android:src="@mipmap/contacts_green"/>
<ImageView
android:id="@+id/image2"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_horizontal"
android:src="@mipmap/contacts"/>

</FrameLayout>
<TextView
android:id="@+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="芳草"/>

</LinearLayout>

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="vertical"
>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
<ImageView
android:id="@+id/image3_top"
android:layout_width="40dp"
android:layout_height="40dp"
android:tint="@android:color/transparent"
android:layout_gravity="center_horizontal"
android:src="@mipmap/discover_green"/>

/>
<ImageView
android:layout_gravity="center_horizontal"
android:id="@+id/image3"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/discover"
/>
<ImageView
android:layout_gravity="center_horizontal"
android:id="@+id/image3_white"
android:layout_width="40dp"
android:layout_height="40dp"
android:tint="@android:color/white"
android:src="@mipmap/discover_white"/>

</FrameLayout>
<TextView
android:id="@+id/text3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="孤城"/>
</LinearLayout>
</LinearLayout>

</FrameLayout>


Activity代码

package com.why.drawabletinttest;

import android.app.Activity;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.os.Bundle;
import android.util.Log;
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;

/**
* @author wanghuayan
*/
public class PagerActivity extends Activity {
private ViewPager mViewPager;
//灰色以及相对应的RGB值
private int mGrayColor;
private int mGrayRed;
private int mGrayGreen;
private int mGrayBlue;
//绿色以及相对应的RGB值
private int mGreenColor;
private int mGreenRed;
private int mGreenGreen;
private int mGreenBlue;
private List<TextView> textViews;//viewpager中适配的 item
private ImageView[] mBorderimageViews;  //外部的边框
private ImageView[] mContentImageViews; //内部的内容
private ImageView[] mWhiteImageViews;  //发现上面的白色部分
private TextView[] mTitleViews;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pager);
initColor();
mViewPager = (ViewPager) findViewById(R.id.pager_view);
ImageView imageView1 = (ImageView) findViewById(R.id.image1);
ImageView imageView2 = (ImageView) findViewById(R.id.image2);
ImageView imageView3 = (ImageView) findViewById(R.id.image3);
mBorderimageViews = new ImageView[]{imageView1, imageView2, imageView3};
TextView textView = new TextView(PagerActivity.this);
TextView textView1 = new TextView(PagerActivity.this);
TextView textView2 = new TextView(PagerActivity.this);
textViews = new ArrayList<>();
textViews.add(textView);
textViews.add(textView1);
textViews.add(textView2);

ImageView topImageView1 = (ImageView) findViewById(R.id.image1_top);
ImageView topImageView2 = (ImageView) findViewById(R.id.image2_top);
ImageView topImageView3 = (ImageView) findViewById(R.id.image3_top);
mContentImageViews = new ImageView[]{topImageView1, topImageView2, topImageView3};

ImageView whiteImageView1 = (ImageView) findViewById(R.id.image1_white);
ImageView whiteImageView2 = (ImageView) findViewById(R.id.image2_white);
ImageView whiteImageView3 = (ImageView) findViewById(R.id.image3_white);
mWhiteImageViews = new ImageView[]{whiteImageView1, whiteImageView2, whiteImageView3};

TextView titileView1 = (TextView) findViewById(R.id.text1);
TextView titileView2 = (TextView) findViewById(R.id.text2);
TextView titileView3 = (TextView) findViewById(R.id.text3);
mTitleViews = new TextView[]{titileView1, titileView2, titileView3};

ItemPagerAdapter adapter = new ItemPagerAdapter(textViews);
mViewPager.setAdapter(adapter);
setSelection(0);

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (positionOffset > 0) {
if (positionOffset < 0.5) {
//  滑动到一半前,上一页的边框保持绿色不变,下一页的边框由灰色变为绿色
mBorderimageViews[position].setColorFilter(mGreenColor, PorterDuff.Mode.SRC_IN);
mBorderimageViews[position + 1].setColorFilter(getGrayToGreen(positionOffset), PorterDuff.Mode.SRC_IN);
//   上一页的内容保持由实心变为透明,下一页的内容保持透明
mContentImageViews[position].setAlpha((1 - 2 * positionOffset));
mContentImageViews[position + 1].setAlpha(0f);
//文字颜色变化
mTitleViews[position].setTextColor(mGreenColor);
mTitleViews[position + 1].setTextColor(getGrayToGreen(positionOffset));

} else {
//滑动到一半后,上一页的边框由绿变为灰色,,下一页边框保持绿色不变
mBorderimageViews[position].setColorFilter(getGreenToGray(positionOffset), PorterDuff.Mode.SRC_IN);
mBorderimageViews[position + 1].setColorFilter(mGreenColor, PorterDuff.Mode.SRC_IN);
//上一页的内容保持透明,下一页的内容由透明变为实心绿色
mContentImageViews[position].setAlpha(0f);
mContentImageViews[position + 1].setAlpha(2 * positionOffset - 1);
mTitleViews[position].setTextColor(getGreenToGray(positionOffset));
mTitleViews[position + 1].setTextColor(mGreenColor);
if (position > 0.8) {
mWhiteImageViews[position + 1].setVisibility(View.VISIBLE);
mWhiteImageViews[position + 1].setAlpha(10 * positionOffset - 8);
} else {
mWhiteImageViews[position + 1].setVisibility(View.GONE);
}
}
}

}

@Override
public void onPageSelected(int position) {
setSelection(position);

}

@Override
public void onPageScrollStateChanged(int state) {

}
});

}

/**
* 设置索引  当前导航页边框绿色,内容实心绿,其他页边框灰色,内容透明
*
* @param position
*/
private void setSelection(int position) {
for (int i = 0; i < mBorderimageViews.length; i++) {
if (i == position) {
mBorderimageViews[i].setColorFilter(mGreenColor, PorterDuff.Mode.SRC_IN);
mContentImageViews[i].setAlpha(1f);
mWhiteImageViews[i].setVisibility(View.VISIBLE);
mTitleViews[i].setTextColor(mGreenColor);
} else {
mBorderimageViews[i].setColorFilter(mGrayColor, PorterDuff.Mode.SRC_IN);
mContentImageViews[i].setAlpha(0f);
mWhiteImageViews[i].setVisibility(View.GONE);
mTitleViews[i].setTextColor(mGrayColor);
}
}
}

private void initColor() {
mGrayColor = getResources().getColor(R.color.gray);
mGrayRed = Color.red(mGrayColor);
mGrayGreen = Color.green(mGrayColor);
mGrayBlue = Color.blue(mGrayColor);
mGreenColor = getResources().getColor(R.color.green);
mGreenRed = Color.red(mGreenColor);
mGreenGreen = Color.green(mGreenColor);
mGreenBlue = Color.blue(mGreenColor);
}

/**
* 偏移量在 0——0.5区间 ,左边一项颜色不变,右边一项颜色从灰色变为绿色,根据两点式算出RGB变化函数,组合出颜色
*
* @param positionOffset
* @return
*/
private int getGrayToGreen(float positionOffset) {
int red = (int) (positionOffset * (mGreenRed - mGrayRed) * 2 + mGrayRed);
int green = (int) (positionOffset * (mGreenGreen - mGrayGreen) * 2 + mGrayGreen);
int blue = (int) ((positionOffset) * (mGreenBlue - mGrayBlue) * 2 + mGrayBlue);
Log.d("why ", "#### " + red + "  " + green + "  " + blue);
return Color.argb(255, red, green, blue);
}

/**
* 偏移量在 0.5--1 区间,颜色从绿色变成灰色,根据两点式算出变化RGB随偏移量变化函数,组合出颜色
*
* @param positionOffset
* @return
*/
private int getGreenToGray(float positionOffset) {
int red = (int) (positionOffset * (mGrayRed - mGreenRed) * 2 + 2 * mGreenRed - mGrayRed);
int green = (int) (positionOffset * (mGrayGreen - mGreenGreen) * 2 + 2 * mGreenGreen - mGrayGreen);
int blue = (int) (positionOffset * (mGrayBlue - mGreenBlue) * 2 + 2 * mGreenBlue - mGrayBlue);
Log.d("why ", "#### " + red + "  " + green + "  " + blue);
return Color.argb(255, red, green, blue);
}

/**
* viewpager适配器
*/
class ItemPagerAdapter extends PagerAdapter {
List<TextView> list;

public ItemPagerAdapter(List<TextView> views) {
list = views;
}

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

@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
switch (position) {
case 0:
list.get(position).setText("晴川历历汉阳树");
break;
case 1:
list.get(position).setText("芳草萋萋鹦鹉洲");
break;
case 2:
list.get(position).setText("长烟落日孤城闭");
break;
}
list.get(position).setGravity(Gravity.CENTER);

container.addView(list.get(position), 0);
return list.get(position);
}

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


到此为止,微信滑动导航卡就完成了,不需要自定义,用简单的imageview就可以完成,效果也是非常好,可能有点难度的就是滑动过程中颜色的计算,就是高中直线的两点式计算,这里换个马甲,有人可能有些迷惑,颜色可以分解为RGB三色,分别算出当前偏移量上的RGB,在组合在一起。举个例子,在0.5到1之间,颜色从 绿色变为灰色,列个函数(color-绿)/(position-0.5)=(灰-绿)/(1-0.5),算出color和 position函数,置换为对应的RGB变化。

demo下载地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  微信 着色