Android 模仿搜狐新闻的ViewpagerIndicator

好久没写博客了,今天没事怒更一记。

如标题今天我们来模仿一下搜狐新闻,先上个效果图.

技术分享

效果图上完之后再上个博客,我也是从这里得到的启发http://blog.csdn.net/qibin0506/article/details/42046559


1,接下来我们就来分析一下这个效果

下面的内容应该是用viewpager,头部的选项卡我们用一个LinearLayout+HorizontalScrollView也可以实现,滚动效果的话我们可以用到我们学会的scrollTo,大致的思路理清,我们就开始码代码吧!

2,自定义LinearLayout

我们可以观察到那个红色的矩形应该是和整体的LinearLayout在一起,所以我们可以自定义LinearLayout画出那个矩形,滚动就是改变画矩形的位置即可

<span style="font-size:14px;">public class ViewpagerIndicator extends LinearLayout {

    /**
     * 画笔,底部矩形
     */
    private Paint paint;
    /**
     * 子View个数
     */
    private int mCount;
    private int mTop;
    private int mWidth;
    private int mLeft;
    private int mHeight = 5;
    public OnTextClick onTextClick;
    public ViewpagerIndicator(Context context) {
        super(context);
    }

    public ViewpagerIndicator(Context context, AttributeSet attrs) {
        super(context, attrs);
        setBackgroundColor(Color.TRANSPARENT);
        paint = new Paint();
        paint.setColor(getResources().getColor(R.color.red));
        paint.setAntiAlias(true);

    }

    public interface OnTextClick{
       public void textViewClick(int position);
    }

    public void setOnTextClick(OnTextClick onTextClick){
        this.onTextClick = onTextClick;

    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //整体高度
        mTop = getMeasuredHeight();
        //整体宽度
        int width = getMeasuredWidth();
        //加上指示器高度
        int height = mTop + mHeight;
        //指示器的宽度
        mWidth = width / mCount;
        setMeasuredDimension(width, height);
    }</span>

在onMeasure中我们拿到我们想要的关于位置距离的值,接下来我们就可以在onDraw中画出想要的矩形了

<span style="font-size:14px;">/**
     * 画出指示器
     * @param canvas
     */
    protected void onDraw(Canvas canvas) {
        Rect rect = new Rect(mLeft, mTop, mLeft + mWidth, mTop + mHeight);
        canvas.drawRect(rect, paint);
    }</span>

没错,就是俩句代码,之后通过不断的改变left就可以实现跟随滚动的效果。

<span style="font-size:14px;">  /**
     * 滚动效果
     * @param position
     * @param positionOffset
     */
    public void scrollTo(int position, float positionOffset) {
        mLeft = (int) ((position + positionOffset) * mWidth);
        postInvalidate();
    }</span>
方法中的俩个参数我们可以通过viewpager的回调方法onPageScrolled中得到,position就是当前的选项卡,positionOffset相当于滑动的百分比0.0~0.0之间.

3,使用自定义控件

ok,大致的思路都已经理清,接下来我们就可以使用自己做的Indicator了。

<span style="font-size:14px;"><RelativeLayout 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"
    tools:context="topnews.inhome.wrh.fragment.NewsFragment">

    <HorizontalScrollView
        android:id="@+id/horizon"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:scrollbars="none"
        >
        <com.wrh.sohunews.ViewpagerIndicator
            android:id="@+id/indicator"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:paddingBottom="10dp"
            android:paddingTop="10dp">

            <TextView
                android:layout_width="0dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:layout_margin="10dp"
                android:text="首页"
                android:textColor="@color/red"
                android:gravity="center" />

            <TextView
                android:layout_width="0dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:text="订阅"
                android:layout_margin="10dp"
                android:gravity="center" />

            <TextView
                android:layout_width="0dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:text="娱乐"
                android:layout_margin="10dp"
                android:gravity="center" />

            <TextView
                android:layout_width="0dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:layout_margin="10dp"
                android:text="本地"
                android:gravity="center" />

            <TextView
                android:layout_width="0dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:layout_margin="10dp"
                android:text="生活"
                android:gravity="center" />

            <TextView
                android:layout_width="0dp"
                android:layout_margin="10dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:text="军事"
                android:gravity="center" />

            <TextView
                android:layout_width="0dp"
                android:layout_margin="10dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:text="财经"
                android:gravity="center" />

            <TextView
                android:layout_width="0dp"
                android:layout_margin="10dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:text="社会"
                android:gravity="center" />
            <TextView
                android:layout_width="0dp"
                android:layout_margin="10dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:text="汽车"
                android:gravity="center" />
            <TextView
                android:layout_width="0dp"
                android:layout_margin="10dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:text="体育"
                android:gravity="center" />
            <TextView
                android:layout_width="0dp"
                android:layout_margin="10dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:text="历史"
                android:gravity="center" />
            <TextView
                android:layout_width="0dp"
                android:layout_margin="10dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:text="搞笑"
                android:gravity="center" />
            <TextView
                android:layout_width="0dp"
                android:layout_margin="10dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:text="情感"
                android:gravity="center" />
        </com.wrh.sohunews.ViewpagerIndicator>
    </HorizontalScrollView>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/horizon"

        />
</RelativeLayout></span>
先不要被眼前多如狗的TextView吓到,其实都是粘贴复制上去的改了一个text就行,我们也可以在自己的LinearLayout中通过TextView t = new TextView();然后add进去,这俩种方法都可以。

但是我们显然都不想给这么多的textview都取id然后拿到它的实例再setOnClick,所以我们就可以在自定义的ViewGroup中直接set

 <span style="font-size:14px;">@Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mCount = getChildCount();

        /**
         * 监听点击事件,回调接口
         */
        for (int i = 0 ;i<mCount;i++){
            View view = getChildAt(i);
            if (view instanceof TextView)
            {
                final int finalI = i;
                ((TextView) view).setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                         if (onTextClick != null) {
                             onTextClick.textViewClick(finalI);
                         }
                    }
                });
            }
        }
    }</span>
通过回调接口我们就轻易的拿到了所点击的是哪一页选项卡,接下来我们来看看使用方法

<span style="font-size:14px;">public class MainActivity extends Activity implements ViewPager.OnPageChangeListener,ViewpagerIndicator.OnTextClick{

    private ViewPager viewPager;
    private int lastX = 0;
    private ViewpagerIndicator indicator;
    private HorizontalScrollView horizontalScrollView;

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

    private void initViews() {
        viewPager = (ViewPager) findViewById(R.id.viewpager);
        indicator = (ViewpagerIndicator) findViewById(R.id.indicator);
        indicator.setOnTextClick(this);
        horizontalScrollView = (HorizontalScrollView) findViewById(R.id.horizon);
        viewPager.setAdapter(new MyAdapter());
        viewPager.setOnPageChangeListener(this);
    }</span>
一些必要的实例化.

<span style="font-size:14px;">  /**
     * 只有当从第一项滚动第二项或者从第二项滚动第一项时,整体不动指示器单独滚动,其余都是整个布局滚动
     * @param position
     * @param positionOffset
     * @param positionOffsetPixels
     */
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (!(position == 0 && positionOffset - lastX >= 0) || !(position == 1 && positionOffset - lastX <= 0)){
            horizontalScrollView.scrollTo((int)((positionOffset+position-1)*indicator.getIndicatorWidth()), 0);
        }
        indicator.scrollTo(position, positionOffset);
        lastX = (int) positionOffset;
    }

    @Override
    public void onPageSelected(int position) {
        indicator.resetTextViewColor();
         indicator.setFocusTextView(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }</span>
通过观察搜狐新闻我们可以看到,当从第一页滑动到第二页时,只有Indicator滚动了,之后都是整体滚动,所以我们要做好判断,整体滚动的时候indicator就相当于停留在了显示出来的第二个选项卡,接下来我们看看实际效果.

技术分享

效果还是阔以的嘛,咔咔。


项目源码


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。