手记

【Android】底部Tab+ViewPager(仿微信界面)

感谢 github的作者:wuyexiong


效果图(图片和文字都有渐变效果)


实现

<br>主要用到自定义一个LinearLayout和ImageView<br><br>
1.BottomIconView继承自ImageView
BottomIconView的作用是现在Tab中的图标,有根据滑动的偏移值显示渐变的图标。


package jfsl.view.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.ImageView;

/**
 * 底部Tab图标类,
 * 分正常的状态和选中的状态
 * 根据滑动的偏移量改变alpha值
 * 然后显示出来
 * <p>
 * 感谢 wuyexiong
 * Created by wuyexiong on 4/25/15.
 * Modify by JFSL on 2016-11-20 20:05
 */
public class BottomIconView extends ImageView
{
    public static final int START_POSITION = 0;
    public static final int ALPHA_MAX = 255;
    //画笔
    private Paint mPaint;
    //选中时的图标
    private Bitmap mIconSelected;
    //未选中时的图标
    private Bitmap mIconNormal;
    //选中时的矩形(限制绘制范围)
    private Rect mRectSelected;
    //未选中时的矩形(限制绘制范围)
    private Rect mRectNormal;
    //当前的alpha值
    private int mAlphaCurrent = 0;

    public BottomIconView(Context context)
    {
        super(context);
    }

    public BottomIconView(Context context,AttributeSet attrs)
    {
        super(context,attrs);
    }

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

    /**
     * 初始化
     *
     * @param normal   正常图标的id
     * @param selected 选中的图标的id
     */
    public final void init(int normal,int selected) throws Exception
    {
        mIconNormal = createBitmap(normal);
        mIconSelected = createBitmap(selected);
        //创建不了图片
        if(mIconNormal == null || mIconSelected == null)
            throw new Exception("icon id can not create1 bitmap");
        //根据创建的位图创建对应的矩形
        mRectNormal = new Rect(START_POSITION,START_POSITION,mIconNormal.getWidth(),mIconNormal.getHeight());
        mRectSelected = new Rect(START_POSITION,START_POSITION,mIconSelected.getWidth(),mIconSelected.getHeight());
        //画笔只要实例化就行,没有什么要求
        mPaint = new Paint(1);
    }

    /**
     * 根据资源id创建的位图
     *
     * @param resId 资源id
     * @return 创建的位图
     */
    private Bitmap createBitmap(int resId)
    {
        return BitmapFactory.decodeResource(getResources(),resId);
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        //画笔为空,直接返回
        if(mPaint == null)
            return;
        //设置当前选中图标的alpha值(逐渐减少)
        mPaint.setAlpha(ALPHA_MAX - mAlphaCurrent);
        canvas.drawBitmap(mIconNormal,null,mRectNormal,mPaint);
        //设置目标图标的alpha值(逐渐减增大)
        mPaint.setAlpha(mAlphaCurrent);
        canvas.drawBitmap(mIconSelected,null,mRectSelected,mPaint);
    }

    /**
     * 改变alpha值
     *
     * @param alpha
     */
    public final void changeSelectedAlpha(int alpha)
    {
        mAlphaCurrent = alpha;
        invalidate();
    }

    /**
     * ViewPager切换时用到
     *
     * @param offset 偏移量
     */
    public final void transformPage(float offset)
    {
        changeSelectedAlpha((int)(ALPHA_MAX * (1 - offset)));
    }
}

2.底部Tab(BottomIndicator继承自LinearLayout)

package jfsl.view.view;

import android.animation.ArgbEvaluator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import jfsl.view.R;

/**
 * 底部指示器
 * Github上的例子,拿来修改了下
 * Created by wuyexiong on 4/25/15.
 * @version 1.0
 * @aduthor JFSL
 * @date 2016/11/14
 */

public class BottomIndicator extends LinearLayout
{
    public static final int ICON_INDEX_NORMAL = 0;
    public static final int ICON_INDEX_SELECTED = 1;
    public static final int DEFALUT_SELECTED_ITEM = 0;
    public static final String COLOR_TEXT_NORMAL = "#FF999999";
    public static final String COLOR_TEXT_SELECTED = "#FF46C01B";
    //文字颜色渐变类
    private ArgbEvaluator mColorEvaluator;
    //正常文本的颜色
    private int mTextNormalColor;
    //选中时文本的颜色
    private int mTextSelectedColor;
    //最后的位置
    private int mLastPosition;
    //选中的位置
    private int mSelectedPosition;
    //选择的偏移量
    private float mSelectionOffset;

    //底部tab文本
    private String mTitles[] = {"微信","通讯录","发现","我"};
    //对应的图标
    private int mIconRes[][] = {
            {R.drawable.icon_main_home_normal,R.drawable.icon_main_home_selected},
            {R.drawable.icon_main_category_normal,R.drawable.icon_main_category_selected},
            {R.drawable.icon_main_service_normal,R.drawable.icon_main_service_selected},
            {R.drawable.icon_main_mine_normal,R.drawable.icon_main_mine_selected}};

    //Item数组
    private View[] mItemLayout;
    //关联的ViewPager
    private ViewPager mViewPager;

    public BottomIndicator(Context context)
    {
        this(context,null);
    }

    public BottomIndicator(Context context,AttributeSet attrs)
    {
        this(context,attrs,0);

    }

    public BottomIndicator(Context context,AttributeSet attrs,int defStyleAttr)
    {
        super(context,attrs,defStyleAttr);
        //初始化
        init();
    }

    /**
     * 初始化
     */
    private void init()
    {
        //实例化颜色渐变类
        mColorEvaluator = new ArgbEvaluator();
        //选中和未选中的文本的颜色
        mTextNormalColor = Color.parseColor(COLOR_TEXT_NORMAL);
        mTextSelectedColor = Color.parseColor(COLOR_TEXT_SELECTED);
    }

    public void setViewPager(ViewPager viewPager)
    {
        //清空所有的view
        removeAllViews();

        mViewPager = viewPager;
        //viewapger数据不为空
        if(viewPager != null && viewPager.getAdapter() != null)
        {
            //设置监听
            viewPager.addOnPageChangeListener(new ViewPagerListener());
            try
            {
                populateTabLayout();
            }catch(Exception e)
            {
                e.printStackTrace();
            }

        }
    }

    /**
     * 生成底部tab
     */
    private void populateTabLayout() throws Exception
    {

        final PagerAdapter adapter = mViewPager.getAdapter();
        final OnClickListener tabClickListener = new TabClickListener();
        //根据adapter中item 的数量生成数组
        mItemLayout = new View[ adapter.getCount() ];
        //遍历
        for(int i = 0;i < adapter.getCount();i++)
        {

            final View tabView = LayoutInflater.from(getContext()).inflate(R.layout.item_bottom_tab,this,false);
            //找不到对应的布局
            if(tabView == null)
                throw new IllegalStateException("tabView is null.");
            mItemLayout[ i ] = tabView;
            //图标
            BottomIconView iconView = (BottomIconView)tabView.findViewById(R.id.bottom_tab_icon);
            iconView.init(mIconRes[ i ][ ICON_INDEX_NORMAL ],mIconRes[ i ][ ICON_INDEX_SELECTED ]);
            //文字
            TextView textView = (TextView)tabView.findViewById(R.id.bottom_tab_text);
            textView.setText(mTitles[ i ]);

            //改变宽度和权重 item平分屏幕
            LayoutParams lp = (LayoutParams)tabView.getLayoutParams();
            lp.width = 0;
            lp.weight = 1;

            tabView.setOnClickListener(tabClickListener);
            addView(tabView);

            if(i == mViewPager.getCurrentItem())
            {
                iconView.transformPage(DEFALUT_SELECTED_ITEM);
                tabView.setSelected(true);
                textView.setTextColor(mTextSelectedColor);
            }
        }
    }

    /**
     * 内部ViewPager监听
     * 外面想监听,自定义一个
     */
    private class ViewPagerListener implements ViewPager.OnPageChangeListener
    {
        //状态
        private int mScrollState;

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

        @Override
        public void onPageSelected(int position)
        {

            for(int i = 0;i < getChildCount();i++)
            {
                //图标
                BottomIconView bottomIcon = ((BottomIconView)mItemLayout[ i ].findViewById(R.id.bottom_tab_icon));
                bottomIcon.transformPage(position == i ? 0 : 1);
                //文本
                TextView bottomText = ((TextView)mItemLayout[ i ].findViewById(R.id.bottom_tab_text));
                bottomText.setTextColor(position == i ? mTextSelectedColor : mTextNormalColor);
            }

            if(mScrollState == ViewPager.SCROLL_STATE_IDLE)
            {
                onViewPagerPageChanged(position,0f);
            }
            //设置选中项
            for(int i = 0, size = getChildCount();i < size;i++)
            {
                getChildAt(i).setSelected(position == i);
            }

        }

        @Override
        public void onPageScrollStateChanged(int state)
        {
            mScrollState = state;
        }
    }

    /**
     * ViewPager的item改变
     *
     * @param position
     * @param positionOffset
     */
    private void onViewPagerPageChanged(int position,float positionOffset)
    {
        mSelectedPosition = position;
        mSelectionOffset = positionOffset;
        if(positionOffset == 0f && mLastPosition != mSelectedPosition)
        {
            mLastPosition = mSelectedPosition;
        }
        invalidate();
    }

    /**
     * 绘制方法
     *
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
        final int childCount = getChildCount();
        if(childCount > 0)
        {
            if(mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1))
            {

                View selectedTab = getChildAt(mSelectedPosition);
                View nextTab = getChildAt(mSelectedPosition + 1);

                View selectedIconView = ((LinearLayout)selectedTab).getChildAt(0);
                View nextIconView = ((LinearLayout)nextTab).getChildAt(0);

                View selectedTextView = ((LinearLayout)selectedTab).getChildAt(1);
                View nextTextView = ((LinearLayout)nextTab).getChildAt(1);

                //draw icon alpha
                if(selectedIconView instanceof BottomIconView && nextIconView instanceof BottomIconView)
                {
                    ((BottomIconView)selectedIconView).transformPage(mSelectionOffset);
                    ((BottomIconView)nextIconView).transformPage(1 - mSelectionOffset);
                }
                /**
                 * 使用ArgbEvaluator类来控制文本的颜色渐变
                 */
                //draw text color
                Integer selectedColor = (Integer)mColorEvaluator.evaluate(mSelectionOffset,
                        mTextSelectedColor,
                        mTextNormalColor);

                Integer nextColor = (Integer)mColorEvaluator.evaluate(1 - mSelectionOffset,
                        mTextSelectedColor,
                        mTextNormalColor);

                if(selectedTextView instanceof TextView && nextTextView instanceof TextView)
                {
                    ((TextView)selectedTextView).setTextColor(selectedColor);
                    ((TextView)nextTextView).setTextColor(nextColor);
                }
            }
        }
    }

    /**
     * Tab的Item点击
     */
    private class TabClickListener implements OnClickListener
    {
        @Override
        public void onClick(View v)
        {
            for(int i = 0;i < getChildCount();i++)
            {
                if(v == getChildAt(i))
                {
                    mViewPager.setCurrentItem(i,false);
                    return;
                }
            }
        }
    }

}

3.主布局(ViewPager+BottomIndicator)

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

    </android.support.v4.view.ViewPager>
    <!--一定要加背景 android:background="@drawable/bg_tab_bottom"-->
    <jfsl.view.view.BottomIndicator
        android:id="@+id/id_bottom_indicator"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:background="@drawable/bg_tab_bottom">
    </jfsl.view.view.BottomIndicator>
</LinearLayout>

4.tab item的布局(BottomIconView+TextView)

<?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:background="@null"
              android:orientation="vertical">

    <jfsl.view.view.BottomIconView
        android:id="@+id/bottom_tab_icon"
        android:layout_width="32dp"
        android:layout_height="28dp"
        android:layout_gravity="center_horizontal"
        android:scaleType="fitCenter" />

    <TextView
        android:id="@+id/bottom_tab_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:textSize="12sp" />

</LinearLayout>

5.Tab的背景 layer-list可以将多个图片按照顺序层叠起来
背景不能去掉,去掉之后看不到渐变的效果

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item>
        <shape
            android:dither="true"
            android:shape="rectangle">
            <solid android:color="#FFDADADA" />
        </shape>
    </item>
    <item android:top="1px">
        <shape
            android:dither="true"
            android:shape="rectangle">
            <solid android:color="#FFFFFFFF" />
            <padding android:top="1px" />
        </shape>
    </item>

</layer-list>

6.测试的Activity

package jfsl.view;

import android.os.Bundle;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;

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

import jfsl.view.adapter.TopViewPagerAdapter;
import jfsl.view.fragment.SimpleFragment;
import jfsl.view.view.BottomIndicator;

public class MainActivity extends AppCompatActivity
{

    private ViewPager mViewPager;
    private List<String> mTitles = Arrays.asList("Fragment-->微信","Fragment-->通讯录","Fragment-->发现","Fragment-->我");
    private List<SimpleFragment> mFragments = new ArrayList<>();
    private FragmentPagerAdapter mPagerAdapter;

    private BottomIndicator mIndicator;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bottom_tab);
        initViews();
        initDatas();
        initEvent();
    }

    private void initViews()
    {
        mViewPager = (ViewPager)findViewById(R.id.id_viewPager);
        mIndicator = (BottomIndicator)findViewById(R.id.id_bottom_indicator);
    }

    private void initDatas()
    {
        for(String title : mTitles)
        {
            mFragments.add(SimpleFragment.newInstance(title));
        }

        mPagerAdapter = new TopViewPagerAdapter(getSupportFragmentManager(),this,mFragments);
        mViewPager.setAdapter(mPagerAdapter);
        mIndicator.setViewPager(mViewPager);
    }

    private void initEvent()
    {

    }

}

7.adapter(ViewPager的Adapter)

package jfsl.view.adapter;

import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

import java.util.List;

import jfsl.view.fragment.SimpleFragment;

/**
 * @version 1.0
 * @aduthor JFSL
 * @date 2016/11/12 0012
 */

public class TopViewPagerAdapter  extends FragmentPagerAdapter
{
    private Context mContext;
    private List<SimpleFragment> mFragments;

    public TopViewPagerAdapter(FragmentManager fm,Context context,List<SimpleFragment> fragments)
    {
        super(fm);
        mContext = context;
        mFragments = fragments;
    }
    @Override
    public Fragment getItem(int position)
    {
        return mFragments.get(position);
    }

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

8.创建Fragment(根据String创建一个简单的Fragment)

package jfsl.view.fragment;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

/**测试用的Fragment
 * @version 1.0
 * @aduthor JFSL
 * @date 2016/11/12 0012
 */

public class SimpleFragment extends Fragment
{
    public static final String BUNDLE_TITLE = "title";
    private String mTitle;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState)
    {
        Bundle bundle = getArguments();
        if(bundle != null)
            mTitle = bundle.getString(BUNDLE_TITLE);

        TextView textView = new TextView(getActivity());
        textView.setText(mTitle);
        textView.setGravity(Gravity.CENTER);

        return textView;
    }

    /**
     * 获取实例
     * @param title
     * @return
     */
    public static SimpleFragment newInstance(String title)
    {
        Bundle bundle = new Bundle();
        bundle.putString(BUNDLE_TITLE,title);
        SimpleFragment fragment = new SimpleFragment();
        fragment.setArguments(bundle);
        return fragment;
    }
}

代码地址 http://114.215.171.206/14551100130/android_view/bottomtab.zip


图标素材( 放在xxhdpi)







4人推荐
随时随地看视频
慕课网APP

热门评论

挺不错的一个分享,虽说我没有学过android开发,不过还是有学习借鉴意义的。

之前有基于html5+css3开发的仿微信聊天项目|仿微信语音效果,也是挺不错的一个项目。

addOnPageChangeListener和getSupportFragmentManager()报错,什么原因呢 求博主回复一下

查看全部评论