【Android - 自定义View】之自定义颜色渐变的Tab导航栏

  首先来介绍一下这个自定义View:

  • (1)这个自定义View的名称叫做 GradientTab ,继承自View类;
  • (2)这个自定义View实现了颜色渐变的Tab导航栏(仿微信主菜单),用户在左右滑动的时候,当前页对应的Tab逐渐变淡,目标页的Tab逐渐变深;
  • (3)用户可以在XML布局中自定义变色的颜色、图标、文本、文本大小、文本颜色、图文间隔等属性。

  接下来简单介绍一下在这个自定义View中用到的技术点:

  • (1)自定义属性;
  • (2)在 onMeasure() 方法中对View进行测量;
  • (3)在 onLayout() 方法中进行一些在获取到测量值之后才能进行的操作,避免代码多次调用影响性能;
  • (4)使用 Canvas 、 Paint 、 Bitmap 类对自定义View进行绘制;
  • (5)通过 onSaveInstanceState() 和 onRestoreInstanceState() 方法保存和重置View状态(透明度)。

  下面是这个自定义View—— GradientTab 的实现代码:

  自定义View类 GradientTab.java 中的代码:

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

/**
 * 与ViewPager连用的拖动可渐变色的Tab按钮
 */
public class GradientTab extends View {
    private int width, height; // View最终显示的宽高

    private int backColor = Color.GREEN; // 自定义属性:Tab按钮的背景颜色
    private int spacing = -1; // 自定义属性:Tab按钮中图标和文本的间隔
    private StringBuffer text = new StringBuffer(); // 自定义属性:Tab按钮中显示的文本
    private int textColor = Color.BLACK; // 自定义属性:Tab按钮中文本的颜色
    private int textSize = -1; // 自定义属性:Tab按钮的文本大小

    private Bitmap tabBitmap; // 绘制元素的Bitmap
    private Bitmap tmpBm; // 临时Bitmap
    private Paint tabPaint; // 绘制元素的画笔
    private Paint iconPaint; // 绘制图标的画笔
    private Paint textPaint; // 绘制文本的画笔
    private Bitmap iconBm; // 图标图片对应的Bitmap

    private float iconWidth; // 图标图片的宽度
    private float iconHeight; // 图标图片的高度

    private int alpha = 0; // 透明度

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

    public GradientTab(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GradientTab(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // 加载自定义属性
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.GradientTab, defStyleAttr, 0);
        int count = array.getIndexCount();
        for (int i = 0; i < count; i++) {
            int attr = array.getIndex(i);
            switch (attr) {
                case R.styleable.GradientTab_backColor:
                    backColor = array.getColor(attr, Color.GREEN);
                    break;
                case R.styleable.GradientTab_iconImg:
                    int iconRes = array.getResourceId(attr, -1); // 自定义属性:Tab按钮中显示的图标的资源ID
                    if (iconRes != -1) {
                        iconBm = BitmapFactory.decodeResource(getResources(), iconRes);
                        iconWidth = iconBm.getWidth();
                        iconHeight = iconBm.getHeight();
                    }
                    break;
                case R.styleable.GradientTab_spacing:
                    textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                            array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, context.getResources().getDisplayMetrics())),
                            context.getResources().getDisplayMetrics());
                    break;
                case R.styleable.GradientTab_text:
                    text.delete(0, text.length());
                    text.append(array.getString(attr));
                    break;
                case R.styleable.GradientTab_textColor:
                    textColor = array.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.GradientTab_textSize:
                    textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                            array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, context.getResources().getDisplayMetrics())),
                            context.getResources().getDisplayMetrics());
                    break;
            }
        }
        array.recycle();
        // 为一些自定义属性设置默认值
        if (textSize == -1) {
            textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, context.getResources().getDisplayMetrics());
        }
        if (spacing == -1) {
            spacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, context.getResources().getDisplayMetrics());
        }
        // 进行一些对象的初始化操作
        init();
    }

    /**
     * 进行一些对象的初始化操作
     */
    private void init() {
        // 初始化绘制图标的画笔
        iconPaint = new Paint();
        iconPaint.setAntiAlias(true);
        iconPaint.setDither(true);
        // 初始化绘制文本的画笔
        textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setDither(true);
        textPaint.setColor(textColor);
        textPaint.setTextSize(textSize);
        // 初始化绘制元素的画笔
        tabPaint = new Paint();
        tabPaint.setAntiAlias(true);
        tabPaint.setDither(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getMeasuredWidth();
        height = getMeasuredHeight();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        // 初始化绘制元素的画布
        tabBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas tabCanvas = new Canvas(tabBitmap);
        // 对图标进行缩放
        int widthLeft = width - getPaddingLeft() - getPaddingRight();
        int heightLeft = height - getPaddingTop() - getPaddingBottom() - textSize - spacing;
        float scale = (float) Math.min(widthLeft * 1.0 / iconWidth, heightLeft * 1.0 / iconHeight);
        Matrix matrix = new Matrix();
        matrix.postScale(scale, scale);
        iconBm = Bitmap.createBitmap(iconBm, 0, 0, (int) iconWidth, (int) iconHeight, matrix, true);
        iconWidth *= scale;
        iconHeight *= scale;
        // 将图标和文本绘制到绘制元素的画布上
        tabCanvas.drawBitmap(iconBm, (widthLeft - iconWidth) / 2, getPaddingTop(), iconPaint);
        int textWidth = (int) textPaint.measureText(text.toString());
        tabCanvas.drawText(text.toString(), (widthLeft - textWidth) / 2, height - getPaddingBottom(), textPaint);
        tmpBm = tabBitmap.copy(Bitmap.Config.ARGB_8888, false);
        tabPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        tabPaint.setColor(backColor);
        tabCanvas.drawRect(0, 0, width, height, tabPaint);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(tmpBm, 0, 0, null);
        // 绘制图标和文本
        Paint paint = new Paint();
        paint.setAlpha(alpha);
        canvas.drawBitmap(tabBitmap, 0, 0, paint);
    }

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

    /**
     * 当这个View所在的Activity临时结束时,保存当前状态(透明度)
     */
    @Override
    protected Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        bundle.putParcelable("default", super.onSaveInstanceState());
        bundle.putInt("alpha", alpha);
        return bundle;
    }

    /**
     * 当这个View所在的Activity重新打开时,重置当前状态(透明度)
     */
    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            Bundle bundle = (Bundle) state;
            alpha = bundle.getInt("alpha");
            super.onRestoreInstanceState(bundle.getParcelable("default"));
        }
        super.onRestoreInstanceState(state);
    }
}

  自定义属性文件 attr.xml 中的代码:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="backColor" format="color" />
    <attr name="iconImg" format="reference" />
    <attr name="spacing" format="dimension" />
    <attr name="text" format="string" />
    <attr name="textColor" format="color" />
    <attr name="textSize" format="dimension" />

    <declare-styleable name="GradientTab">
        <attr name="backColor" />
        <attr name="iconImg" />
        <attr name="spacing" />
        <attr name="text" />
        <attr name="textColor" />
        <attr name="textSize" />
    </declare-styleable>
</resources>

  主界面布局 activity_main.xml 文件中的代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/gradienttab_main_ly_tabs"
        android:layout_width="match_parent"
        android:layout_height="60.0dip"
        android:layout_alignParentBottom="true"
        android:background="#EEEEEE"
        android:orientation="horizontal">

        <my.itgungnir.custom_gradientmenu.GradientTab
            android:id="@+id/gradienttab_main_tab_tab1"
            android:layout_width="0.0dip"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:padding="3.0dip"
            app:backColor="#008800"
            app:iconImg="@mipmap/menu_tab1"
            app:spacing="2.0dip"
            app:text="Tab1"
            app:textColor="#888888"
            app:textSize="8.0sp" />

        <my.itgungnir.custom_gradientmenu.GradientTab
            android:id="@+id/gradienttab_main_tab_tab2"
            android:layout_width="0.0dip"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:padding="3.0dip"
            app:backColor="#880000"
            app:iconImg="@mipmap/menu_tab2"
            app:spacing="2.0dip"
            app:text="Tab2"
            app:textColor="#888888"
            app:textSize="8.0sp" />

        <my.itgungnir.custom_gradientmenu.GradientTab
            android:id="@+id/gradienttab_main_tab_tab3"
            android:layout_width="0.0dip"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:padding="3.0dip"
            app:backColor="#000088"
            app:iconImg="@mipmap/menu_tab3"
            app:spacing="2.0dip"
            app:text="Tab3"
            app:textColor="#888888"
            app:textSize="8.0sp" />

        <my.itgungnir.custom_gradientmenu.GradientTab
            android:id="@+id/gradienttab_main_tab_tab4"
            android:layout_width="0.0dip"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:padding="3.0dip"
            app:backColor="#888888"
            app:iconImg="@mipmap/menu_tab4"
            app:spacing="2.0dip"
            app:text="Tab4"
            app:textColor="#888888"
            app:textSize="8.0sp" />
    </LinearLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/gradienttab_main_vp_pages"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/gradienttab_main_ly_tabs" />

</RelativeLayout>

  主界面JAVA文件 MainActivity.java 中的代码:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;

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

public class MainActivity extends FragmentActivity {
    private ViewPager viewpager;

    private List<GradientTab> tabList;

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

    @Override
    protected void onResume() {
        super.onResume();
        viewpager = (ViewPager) findViewById(R.id.gradienttab_main_vp_pages);
        initData();
        initEvents();
    }

    private void initData() {
        // 初始化盛放Fragment的列表
        final List<Fragment> list = new ArrayList<>();
        for (int i = 1; i <= 4; i++) {
            PageFragment fragment = new PageFragment();
            Bundle bundle = new Bundle();
            bundle.putString("title", "This is page " + i);
            fragment.setArguments(bundle);
            list.add(fragment);
        }
        // 为ViewPager适配数据
        viewpager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {
                return list.get(position);
            }

            @Override
            public int getCount() {
                return list.size();
            }
        });
        // 初始化四个Tab按钮,添加到List列表中
        tabList = new ArrayList<>();
        tabList.add((GradientTab) findViewById(R.id.gradienttab_main_tab_tab1));
        tabList.add((GradientTab) findViewById(R.id.gradienttab_main_tab_tab2));
        tabList.add((GradientTab) findViewById(R.id.gradienttab_main_tab_tab3));
        tabList.add((GradientTab) findViewById(R.id.gradienttab_main_tab_tab4));
    }

    private void initEvents() {
        // ViewPager的滚动事件
        viewpager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                tabList.get(position).setAlpha(1 - positionOffset);
                tabList.get((int) Math.ceil(position + positionOffset)).setAlpha(positionOffset == 0 ? 1 : positionOffset);
            }

            @Override
            public void onPageSelected(int position) {
            }

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

  运行效果图如下所示:

 

posted on 2017-04-24 15:07  ITGungnir  阅读(1047)  评论(0编辑  收藏  举报

导航