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