仿今日头条的滚动指示器的效果

最近在看今日头条的时候,发现他们的引导的viewpager的指示器跟其他软件有点不同,字体颜色是渐变过去的,所以来模仿了下,效果图如下:

最终效果图
首先,分析上面的动态图,其中两个不同的textview,一个颜色是从左往右变化的,另一个是从右往左变化的。
下面  我们先来分别实现下从左往右变化的TextView和颜色从右往左变化的TextView。

首先,自定义一个View继承自TextView,为什么不继承自View?因为TextView 已经帮我们做了很多了,比如测量控件的大小以及一些兼容性问题上,我们在TetxView上做扩展会更省心省力。

下面,我们来自定义一下这个控件:
首先:我们需要两个颜色,一个是变化前的颜色,一个是变化后的颜色。暂且叫他们previousColor(变化前),changeColor(变化后),下面我们就来自定义下这个属性:

    <declare-styleable name="ColorChangeTextView">
        <!-- 修改之前的颜色 -->
        <attr name="previousColor" format="color"/>
        <!-- 修改之后的颜色 -->
        <attr name="changeColor" format="color"/>
    </declare-styleable>

第二步,在布局中使用这个属性

<com.justh.dell.colorchangetextviewdemo.ColorChangeTextView
        android:id="@+id/colorchangetextview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello JUSTH"
        android:layout_marginTop="30dp"
        android:layout_marginLeft="20dp"
        android:textSize="30dp"
        app:previousColor="@color/colorPrimaryDark"
        app:changeColor="@color/colorAccent"/>

在这里,我想大家应该能够感受到继承自TextView的好处了吧 ,textSize啥的属性随便用啊,老铁!

第三步,下面,我们就该在自定义DE View中获取这些属性值,并设置属性值到对应的使用位置

public class ColorChangeTextView extends TextView {

    private int mPreviousColor = Color.BLUE;
    private int mChangeColor = Color.RED;

    private Paint mPreviousPaint;
    private Paint mChangePaint;
    private Rect bounds = new Rect();

    private float currentOffset = 0.0f;

    //当前颜色变化的方向
    private Direction mDirection = Direction.LEFT_TO_RIGHT;

    //表示颜色变化的方向
    public enum Direction{
        LEFT_TO_RIGHT,RIGHT_TO_LEFT;
    }

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

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

    public ColorChangeTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //获取自定义属性
        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.ColorChangeTextView);

        mPreviousColor = typedArray.getColor(R.styleable.ColorChangeTextView_previousColor,getTextColors().getDefaultColor());
        mChangeColor = typedArray.getColor(R.styleable.ColorChangeTextView_changeColor,getTextColors().getDefaultColor());

        //回收
        typedArray.recycle();

        //设置画笔
        mPreviousPaint = getColorPaint(mPreviousColor);
        mChangePaint = getColorPaint(mChangeColor);

        bounds = new Rect();
    }

    private Paint getColorPaint(int color){
        Paint paint = new Paint();
        paint.setColor(color);
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setTextSize(60);
        return paint;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //自己来画,调用TextView的画的方法,就会把文字都画上同一种颜色
        //super.onDraw(canvas);

        if(mDirection == Direction.LEFT_TO_RIGHT){
            drawText(canvas,mChangePaint,0,(int) (getWidth()*currentOffset));
            drawText(canvas,mPreviousPaint,(int) (getWidth()*currentOffset),getWidth());
        }else{
            drawText(canvas,mChangePaint,(int) (getWidth()*(1-currentOffset)),getWidth());
            drawText(canvas,mPreviousPaint,0,(int) (getWidth()*(1-currentOffset)));
        }
    }

    private void drawText(Canvas canvas,Paint paint,int start,int end){

        canvas.save();

        String text = getText().toString();
        //默认画在中心位置上
        paint.getTextBounds(text,0,text.length(),bounds);

        //计算基线
        Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
        int dy = (fontMetricsInt.bottom - fontMetricsInt.top)/2 - fontMetricsInt.bottom;
        int baseLine = getHeight()/2 + dy;

        Rect rect = new Rect(start,0, end,getHeight());
        canvas.clipRect(rect);
        canvas.drawText(text,getWidth()/2-bounds.width()/2,baseLine,paint);
        
        //记得在裁剪操作之后,还原到之前的状态  这样  才能继续使用
        canvas.restore();
    }

    //设置当前颜色变化的百分比
    public void setCurrentOffset(float currentOffset) {
        this.currentOffset = currentOffset;
        invalidate();
    }

    //设置当前颜色变化的方向
    public void setDirection(Direction direction) {
        mDirection = direction;
    }

    //设置变化的颜色
    public void setChangeColor(int changeColor) {
        this.mChangePaint.setColor(changeColor);
    }

    //设置没变的颜色
    public void setPreviourColor(int previourColor) {
        this.mPreviousPaint.setColor(previourColor);
    }
}
代码中,使用了一个canvas.clipRect()的函数,该函数是截取了画布的一块区域,来显示对应的该区域的操作!

下面  我们在Activity中 设置了两个方向的执行按钮,使用属性动画让效果动起来:

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private Button left_right;
    private Button right_left;
    private ColorChangeTextView colorChangeTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        left_right = (Button) findViewById(R.id.left_right);
        right_left = (Button) findViewById(R.id.right_left);
        colorChangeTextView = (ColorChangeTextView) findViewById(R.id.colorchangetextview);
        left_right.setOnClickListener(this);
        right_left.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.left_right:
                //从左到右
                leftToRight();
                break;
            case R.id.right_left:
                //从右到左
                rightToLeft();
                break;
        }
    }

    public void leftToRight(){
        colorChangeTextView.setDirection(ColorChangeTextView.Direction.LEFT_TO_RIGHT);
        ValueAnimator animator = ObjectAnimator.ofFloat(0,1);
        animator.setDuration(2000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float offset = (float) animation.getAnimatedValue();
                colorChangeTextView.setCurrentOffset(offset);
            }
        });
        animator.start();
    }

    public void rightToLeft(){
        colorChangeTextView.setDirection(ColorChangeTextView.Direction.RIGHT_TO_LEFT);
        ValueAnimator animator = ObjectAnimator.ofFloat(0,1);
        animator.setDuration(2000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float offset = (float) animation.getAnimatedValue();
                colorChangeTextView.setCurrentOffset(offset);
            }
        });
        animator.start();
    }
}

下面,就是效果了,请看大屏幕,哈哈哈!

到这里,革命就已经成功一大半了!下面,只需要把功能引用到Demo里面去一年就差不许多了。

像今日头条这种的效果,很明显是使用了ViewPager+Indicator的配合,所以 我们这里也使用ViewPager+Indicator 的做法,布局如下:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.justh.dell.colorchangetextviewdemo.ViewPagerActivity">

    <!-- 存放变色TextView -->
    <LinearLayout
        android:id="@+id/scroll_linearlayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="10dp"
        android:paddingBottom="10dp"
        android:orientation="horizontal"/>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>


然后在ViewPager中放置fragment,用来展示信息,这里,就使用TextView来代替吧!

fragment:

public class ItemFragment extends Fragment {

    private String title;

    public static ItemFragment newInstance(String item) {
        ItemFragment itemFragment = new ItemFragment();
        Bundle bundle = new Bundle();
        bundle.putString("title", item);
        itemFragment.setArguments(bundle);
        return itemFragment;
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_item, container, false);
        TextView textView = (TextView) view.findViewById(R.id.textView);
        Bundle bundle = getArguments();
        textView.setText(bundle.getString("title"));
        return view;
    }

}


ViewPagerAvtivity:

public class ViewPagerActivity extends AppCompatActivity {
    private String[] items = {"推荐", "热点", "视频", "社会", "订阅", "娱乐"};
    private LinearLayout mIndicatorContainer;
    private ViewPager viewPager;
    private List<ColorChangeTextView> mIndicators;

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

        mIndicators = new ArrayList<>();

        mIndicatorContainer = (LinearLayout) findViewById(R.id.scroll_linearlayout);
        viewPager = (ViewPager) findViewById(R.id.viewpager);

        //初始化ViewPager对应的指示器
        initIndicator();
        
        //初始化Viewpager
        initViewPager();
    }

    /**
     * 初始化ViewPager
     */
    private void initViewPager() {
        viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {
                return ItemFragment.newInstance(items[position]);
            }

            @Override
            public int getCount() {
                return items.length;
            }

            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {

            }
        });

        //设置ViewPager 滚动监听
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                // position 代表当前的位置
                // positionOffset 代表滚动的 0 - 1 百分比

                // 1.左边  位置 position
                ColorChangeTextView left = mIndicators.get(position);
                left.setDirection(ColorChangeTextView.Direction.RIGHT_TO_LEFT);
                left.setCurrentOffset(1-positionOffset);

                try {
                    ColorChangeTextView right = mIndicators.get(position + 1);
                    right.setDirection(ColorChangeTextView.Direction.LEFT_TO_RIGHT);
                    right.setCurrentOffset(positionOffset);
                }catch (Exception e){

                }
            }

            @Override
            public void onPageSelected(int position) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
    }


    /**
     * 初始化可变色的指示器
     */
    private void initIndicator() {
        for (int i = 0; i < items.length; i++) {
            // 动态添加颜色跟踪的TextView
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
            params.weight = 1;
            ColorChangeTextView colorTrackTextView = new ColorChangeTextView(this);
            // 设置颜色
            colorTrackTextView.setTextSize(20);
            colorTrackTextView.setChangeColor(Color.RED);
            colorTrackTextView.setText(items[i]);
            colorTrackTextView.setLayoutParams(params);
            // 把新的加入LinearLayout容器
            mIndicatorContainer.addView(colorTrackTextView);
            // 加入集合
            mIndicators.add(colorTrackTextView);
        }
    }
}


代码中,相信大家看注释,就应该很明了了,这里就不过多解释了,主要的代码还是在字体颜色的变换上面!
最后,再看下最终效果图:
最终效果图


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值