自定义随动indicator附带title颜色渐变

来源:互联网 发布:巨人网络 校招 编辑:程序博客网 时间:2024/06/07 09:37

1. 前言

在观看了#慕课网#hyman讲师所制作的视频《Android-自定义ViewPager指示器》之后,公司刚好遇到了类似的需求,于是自己按照hyman所讲根据自己的需求写了这个指示器,优化了一些代码和加入了简单的动画和title的点击联动部分,希望这个demo能帮到有类似需求的朋友。如果对此控件思路不太熟的朋友,可以看下这个视频讲解,本文是在此视频上做扩展说明。

《Android-自定义ViewPager指示器》链接地址:http://www.imooc.com/learn/615

2. 自定义随动指示器

先上效果图:
indicator

1. 基本思路:

一般我们在做类似的指示器的时候,都会先做指示器,也就是title部分然后再做viewpager部分,通过viewpager联动title实现对应的变化。当然本文的思路也基本类似,通过将title部分进行封装,并将viewpager的OnPageChangeListener中的三个方法中的对应参数传入指示器内,让指示器内部进行相应的显示处理。代码如下:

package com.example.titleindicator;import java.util.ArrayList;import com.example.titleindicator.CustomizeIndicator.OnTitleClickListener;import android.app.Activity;import android.os.Bundle;import android.support.v4.view.ViewPager;import android.support.v4.view.ViewPager.OnPageChangeListener;import android.view.LayoutInflater;import android.view.View;public class MainActivity extends Activity implements OnPageChangeListener, OnTitleClickListener {    private CustomizeIndicator indicator;    private ViewPager viewpager;    private MyViewPagerAdapter adapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initalWeight();        initialIndicator();    }    private void initialIndicator() {        //第一步:找到该控件        indicator = (CustomizeIndicator) findViewById(R.id.myCustomizeIndicator);        //第二步:设置title的显示文字        ArrayList<String> titleName = new ArrayList<String>();        titleName.add("TitleOne");        titleName.add("TitleTwo");        titleName.add("TitleThree");        titleName.add("TitleFour");        titleName.add("TitleFive");        titleName.add("TitleSix");        indicator.setTitle(titleName);        //第三步:设置indicator的点击事件        indicator.setOnTitleClickListener(this);    }    private void initalWeight() {        viewpager = (ViewPager) findViewById(R.id.myViewPager);        LayoutInflater inflater = getLayoutInflater();        View view1 = inflater.inflate(R.layout.views, null);        view1.setBackgroundColor(        getResources().getColor(R.color.red_color));        View view2 = inflater.inflate(R.layout.views, null);        getResources().getColor(R.color.green_color));        View view3 = inflater.inflate(R.layout.views, null);        view3.setBackgroundColor(        getResources().getColor(R.color.blue_color));        View view4 = inflater.inflate(R.layout.views, null);        getResources().getColor(R.color.purple_color));        View view5 = inflater.inflate(R.layout.views, null);        view5.setBackgroundColor(        getResources().getColor(R.color.orange_color));        View view6 = inflater.inflate(R.layout.views, null);        view6.setBackgroundColor(        getResources().getColor(R.color.black_color));        ArrayList<View> viewList = new ArrayList<View>();        viewList.add(view1);        viewList.add(view2);        viewList.add(view3);        viewList.add(view4);        viewList.add(view5);        viewList.add(view6);        adapter = new MyViewPagerAdapter(viewList);        viewpager.setAdapter(adapter);        viewpager.setOnPageChangeListener(this);    }    //第四步:在viewpager的OnpageChangeListener的三个方法中    //分别添加indicator的对应的三个方法    @Override    public void onPageScrollStateChanged(int state) {        indicator.setOnPageScrollStateChanged(state);    }    @Override    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {        indicator.setOnPageScrolled(position, positionOffset);    }    @Override    public void onPageSelected(int position) {        indicator.setOnPageSelected(position);    }    @Override    public void TitleClick(View v, int position) {        //View v是对应的那个title,position是title所在的位置,跟传入位置一致        viewpager.setCurrentItem(position);    }

从上面代码中我们可以知道,title部分是按照你传入的titleName个数来自动生成的,所以对应Tab的位置也是按照传入的titleName顺序而定。指示器中点击的每个Tab,也会通过方法TitleClick回调出来,参数View v和int position就是你所点击的那个Tab和它所在的位置。

以上就是通常我们所用到的需求,当然如果还需要在指示器中做一些其他的需求,可以在后续进行修改。

接下来就是两个关键的部分!

2.字体颜色的渐变思路

在效果图中,字体的颜色改变是通过手指滑动的时候进行渐变。当选中的位置时titleOne,然后准备滑动到titleTwo的过程中,titleOne的颜色由白色逐渐加深至黑色,titleTwo的颜色相反变化,很明显,在这动态变换的过程中,肯定有一个动态值来控制颜色的逐渐改变,这个动态值就是在ViewPager的onPageScrolled中的positionOffset这个值。这个值变化是个百分比(0.00-1.00),也是我们在滑动viewpager过程中,红色移动滑块跟着随动的关键值,在后续我们将会用到。

对于颜色是如何渐变的,网上有很多此类文章都介绍很详细,这里就不在赘述了。

3.红色滑块随动的基本思路

在上面思路中提到,positionOffset是唯一能根据viewpager改变进行精确联动的值,我们暂且叫它“精确值”,当用手指滑动Viewpager时,Viewpager中的onPageScrolled会在滑动过程中不停被调用,在代码中,我们将对应的positionOffset值传给了indicator,所以indicator也会在同时不停的调用 indicator.setOnPageScrolled(position, positionOffset);这个方法,只要在这个方法中不停重绘改变滑块的位置,就能达到滑块随动的效果。

简单来说,在#慕课网#《Android-自定义ViewPager指示器》这个教程中,随动的是一个绘制的三角形,而在我们的需求中,只需要将这个三角形变成绘制的红色矩形倒角的滑块就可以了。

4indicator代码

在介绍完基本思路后,这里贴上indicator的代码供大家参考:

public class CustomizeIndicator extends LinearLayout implements OnClickListener{    private Context mContext;    /**可见TAB数量*/    private int VISIBLE_TAB = 4;    /**当前页面位置*/    private int currentPosition = 0;    /**title*/    private ArrayList<String> titleList;    /**title view*/    private ArrayList<TextView> textViewList;    /**屏幕的宽度*/    private int screenWidth;    /**矩形宽度比*/    private float RADIO_RECT_WIDTH = 3/4f;    /**矩形高度比*/    private float RADIO_RECT_HEIGHT = 2/3f;    private Paint picPaint;    private Path path;    private float rectWidth;    private float rectHeight;    private float initTranslationX;    private float initY;    private float TranslationX;    /**色值*/    private int textColor = Color.parseColor("#333333");    private int textWhiteColor = Color.parseColor("#FFFFFF");    /**颜色渐变器*/    private ColorGradient gradient;    /**title事件响应接口*/    private OnTitleClickListener listener;    /**是否因为点击而改变tab页面*/    private boolean ifClickToChangePage = false;    public CustomizeIndicator(Context context) {        this(context,null);    }    public CustomizeIndicator(Context context, AttributeSet attrs) {        this(context, attrs,0);    }    @SuppressLint("NewApi")    public CustomizeIndicator(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        mContext = context;        initial();    }    private void initial() {        this.setOrientation(HORIZONTAL);        titleList = new ArrayList<String>();        textViewList = new ArrayList<TextView>();        screenWidth = getScreenWidth();        picPaint = new Paint();        picPaint.setAntiAlias(true);        int picPaintColor = Color.parseColor("#e22319");        picPaint.setColor(picPaintColor);        picPaint.setStyle(Style.FILL);        picPaint.setPathEffect(new CornerPathEffect(dpTopx(mContext, 5)));//倒角        int selectColor = Color.parseColor("#333333");        int unSelectColor = Color.parseColor("#FFFFFF");        gradient = new ColorGradient(unSelectColor,selectColor , 100);    }    /**     * 初始化title组件     * @param titleList     * @return     */    private void getTitleTextView(ArrayList<String> titleList){        if(titleList == null){            return;        }        int num = titleList.size();        for (int i = 0; i < num; i++) {            TextView textview = new TextView(mContext);        LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);            lp.width = screenWidth/VISIBLE_TAB;            textview.setLayoutParams(lp);            textview.setGravity(Gravity.CENTER);            if(0 == i){//初始化第一个字体为白色                textview.setTextColor(textWhiteColor);            }else{                textview.setTextColor(textColor);            }            textview.setTextSize(14);            textview.setText(titleList.get(i));            textview.setTag(i);            textview.setOnClickListener(this);            textViewList.add(textview);        }    }    @Override    public void onClick(View v) {        int viewPosition = (Integer) v.getTag();        ifClickToChangePage = true;        listener.TitleClick(v,viewPosition);//返回textview和当前textview的位置    }    public interface OnTitleClickListener{        void TitleClick(View v, int position);    }    /**     * 获取屏幕宽度     * @return     */    private int getScreenWidth() {        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);        DisplayMetrics outMetrics = new DisplayMetrics();        wm.getDefaultDisplay().getMetrics(outMetrics);        return outMetrics.widthPixels;    }    /**     * dp转化px     * @param context     * @param dpValue     * @return     */    private float dpTopx(Context context,float dpValue){        float scale=context.getResources().getDisplayMetrics().density;        return (dpValue*scale+0.5f);      }       /**     * 初始化矩形     */    private void initialRect() {        path = new Path();        path.moveTo(0, 0);        path.lineTo(rectWidth, 0);        path.lineTo(rectWidth, rectHeight);        path.lineTo(0, rectHeight);        path.close();    }    /**     * 根据滑动程度进行颜色改变     * @param positionOffset      * @param position      */    private float mOffset = 0;    private int originalPosition = 0;//初始位置    private void changeTextColor(int position, float positionOffset){        if(originalPosition == position         && (positionOffset*100) != mOffset){//向右滑动            mOffset = (int)(positionOffset*100);            TextView tv = (TextView) getChildAt(position);            tv.setTextColor(gradient.getColor((int)(positionOffset*100)));//变黑            if((positionOffset*100) > mOffset && position+1 <=getChildCount()){                TextView tv1 = (TextView) getChildAt(position+1);                tv1.setTextColor(gradient.getColor((int)((1-positionOffset)*100)));//变白            }        }else if(originalPosition != position){//向左滑动            if(Math.abs(originalPosition - position) >=2){                TextView tv = (TextView) getChildAt(position);                tv.setTextColor(gradient.getColor(0));//变白                TextView tv1 = (TextView) getChildAt(originalPosition);                tv1.setTextColor(gradient.getColor(100));//变黑                originalPosition = position;            }else{                originalPosition = position;                TextView tv = (TextView) getChildAt(position);                tv.setTextColor(gradient.getColor((int)(positionOffset*100)));//变黑                if(originalPosition != getChildCount()-1){                    TextView tv1 = (TextView) getChildAt(position+1);                    tv1.setTextColor(gradient.getColor((int)((1-positionOffset)*100)));//变白                }            }        }    }    /**     * 根据某个位置改变title颜色     */    private void changeTabColorToPosition(int position){        for (int i = 0; i < textViewList.size(); i++) {            TextView tv = textViewList.get(i);            if(position == i){                tv.setTextColor(textWhiteColor);            }else{                tv.setTextColor(textColor);            }        }        originalPosition = position;    }    /**     * 滑动到指定的tab上并显示正确的颜色     * @param posi      */    private void scrollToPosition(int position){        float mTabWidth= getWidth()/VISIBLE_TAB;        TranslationX = mTabWidth*position;        if(VISIBLE_TAB > 1){            if(getChildCount() > VISIBLE_TAB){                && position > VISIBLE_TAB-2                 && position>currentPosition){                    //                  this.scrollTo((int)((position- (VISIBLE_TAB-2))*tabWidth), 0);                    scrollAnimation(((position - 1)-(VISIBLE_TAB-2))*mTabWidth, (position- (VISIBLE_TAB-2))*mTabWidth);                }else if(position <= 3 && position>=1 && position<currentPosition){                    if(position+1<=currentPosition && position-1>0){                        //                      this.scrollTo((int)((position- (VISIBLE_TAB-2))*tabWidth), 0);                        scrollAnimation((currentPosition- (VISIBLE_TAB-2))*mTabWidth, (position-(VISIBLE_TAB-2))*mTabWidth);                    }else{                        this.scrollTo((int)((position- 1)*mTabWidth), 0);                        scrollAnimation( (currentPosition- (VISIBLE_TAB-2))*mTabWidth, (position-1)*mTabWidth);                    }                }            }        }        changeTabColorToPosition(position);        postInvalidate();    }    /**     * 联动tab     * @param position     * @param positionOffset     */    private void scroll(int position, float positionOffset) {        float tabWidth = getWidth()/VISIBLE_TAB;        TranslationX = tabWidth*(position+positionOffset);        if(VISIBLE_TAB != 1){            if(position >= (VISIBLE_TAB-2)                     && positionOffset>0                     && getChildCount() > VISIBLE_TAB                     && getChildCount() > (position+2)){                if(position == currentPosition){                    this.scrollTo((int)((position - (VISIBLE_TAB-2))*tabWidth)+(int)(tabWidth*positionOffset), 0);                }else if(position<currentPosition ){                    this.scrollTo((int)((position - (VISIBLE_TAB-2))*tabWidth)+(int)(tabWidth*positionOffset), 0);                }            }        }else{            this.scrollTo((int)(position *  tabWidth)+(int)(tabWidth*positionOffset), 0);        }        changeTextColor(position,positionOffset);        postInvalidate();    }    /**     * 设置当前页面(用于点击title时)     * @param i     */    private void setCurrentItem(int position) {        if(position<0 || position > titleList.size()){            return ;        }        int posi = 0;        int titleSize = titleList.size();        if(position > titleSize){            posi = titleSize;        }else{            posi = position;        }        scrollToPosition(posi);        setTabCurrentPage(posi);    }    /**     * 设置当前页     * @param position     */    private void setTabCurrentPage(int position){        if(currentPosition != position){            currentPosition = position;        }    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        if(w != oldw || h != oldh){            rectWidth = w/VISIBLE_TAB*RADIO_RECT_WIDTH;            rectHeight =h*RADIO_RECT_HEIGHT;            initTranslationX = (w/VISIBLE_TAB - rectWidth)/2;            initY = h*RADIO_RECT_HEIGHT/2;            initialRect();        }    }    @Override    protected void dispatchDraw(Canvas canvas) {        canvas.save();        canvas.translate(initTranslationX+TranslationX,(rectHeight - initY)/2);        canvas.drawPath(path, picPaint);        canvas.restore();        super.dispatchDraw(canvas);    }    /**     * 滑动动画     */    @SuppressLint("NewApi")    private void scrollAnimation(float start,float end){        ValueAnimator anim = ValueAnimator.ofFloat(start, end);        anim.setDuration(300);        anim.addUpdateListener(new AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                float currentValue = (Float) animation.getAnimatedValue();                CustomizeIndicator.this.scrollTo((int)currentValue, 0);            }        });        anim.start();    }    //______________API 方法,对外提供______________    /**     * 传入title     * @param titleList     */    public void setTitle(ArrayList<String> titleList) {        if(titleList == null){            return;        }        removeAllViews();        getTitleTextView(titleList);        for (TextView tv:textViewList) {            addView(tv);        }        this.titleList.clear();        this.titleList.addAll(titleList);    }    public void setOnTitleClickListener(OnTitleClickListener listener){        this.listener = listener;    }    //TODO viewpager的OnPageChangeListener三个响应    public void setOnPageScrollStateChanged(int state) {        if(0 == state){//表示滑动结束,什么都没做            if(ifClickToChangePage){                ifClickToChangePage = false;            }        }    }    public void setOnPageScrolled(int position, float positionOffset) {        if(!ifClickToChangePage){            scroll(position,positionOffset);        }    }    public void setOnPageSelected(int position) {        if(!ifClickToChangePage && currentPosition != position){            this.currentPosition = position;        }        if(ifClickToChangePage){            setCurrentItem(position);        }    }

由于我不太喜欢用自定义属性,所以代码中没有加入自定义属性,大家根据实际需求实际添加吧。

在代码中有一个颜色渐变的类,我也贴下此类的代码:

/** * 用于获取两个颜色的过渡色 */public class ColorGradient {    /** 结束的颜色 */    private int color1;    /** 开始的颜色 */    private int color2;    /** 开始的颜色到结束的颜色的过渡色分为几份 */    private int count;    /** 开始的颜色的a,r,g,b值 */    private int[] color1Values;    /** 结束的颜色的a,r,g,b值 */    private int[] color2Values;    public ColorGradient(int color1, int color2, int count) {        super();        this.color1 = color1;        this.color2 = color2;        this.count = count;        color1Values = toColorValue(color1);        color2Values = toColorValue(color2);    }    /**     * 获取第几个过渡色,总共分为count个过渡色,i表示去其中的第i个过渡色     *      * @param i     * @return     */    public int getColor(int i) {        int[] result = new int[4];        for (int j = 0; j < color2Values.length; j++) {            result[j] = (int) (color1Values[j] + (color2Values[j] - color1Values[j]) * 1.0 / count * i);        }        return Color.argb(result[0], result[1], result[2], result[3]);    }    private int[] toColorValue(int color) {        int[] values = new int[4];        values[0] = Color.alpha(color);        values[1] = Color.red(color);        values[2] = Color.green(color);        values[3] = Color.blue(color);        return values;    }    public int getColor1() {        return color1;    }    public int getColor2() {        return color2;    }    public int getCount() {        return count;    }

很简单,主要就是通过动态改变a,r,g,b的值来做到颜色的渐变,在很多图像处理中很常用。

3.结束语

本文讲解的内容不多,主要描述一下实现思路,类似指示器的需求不会太多,只要弄懂这种常用的随动原理和如何实现,就能应付绝大多数的需求。这里有源码链接可供下载
http://download.csdn.net/detail/qq_30227229/9511814

0 0
原创粉丝点击