码迷,mamicode.com
首页 > 其他好文 > 详细

ViewPagerIndicator 仿网易新闻tab或是知乎的发现中的tab

时间:2015-04-12 16:18:41      阅读:607      评论:0      收藏:0      [点我收藏+]

标签:android   网易   新闻   viewpager   canvas   

tab中经常有用到如网易新闻中那样的效果,要求能滑动,下面有指示,当tab页超出屏幕下面的内容也要跟着移动。

指示器的效果很多人都会用到TabPageIndicator,这里我们参考http://blog.csdn.net/lmj623565791/article/details/42160391 来写下自己的指示器。

如图:

技术分享技术分享技术分享

总共有三个部分组成,

HorizontalScrollView里有个LinearLayout可以添加我们的view,

ViewPagerIndicator我们的指示器

ViewPager来放内容fragment

布局如下

<LinearLayout 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"
    android:background="#ffffff"
    android:orientation="vertical" >

    <HorizontalScrollView
        android:id="@+id/hsv"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:scrollbars="@null" >

        <LinearLayout
            android:id="@+id/ll1"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:orientation="horizontal" >
        </LinearLayout>
    </HorizontalScrollView>

    <com.example.demotab.ViewPagerIndicator
        android:id="@+id/indicator"
        android:layout_width="match_parent"
        android:layout_height="5dp"
        android:background="#000000"
        android:orientation="horizontal"
        >
    </com.example.demotab.ViewPagerIndicator>

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

</LinearLayout>
2.重点是ViewPagerIndicator的逻辑处理,我们这里要用到自定义控件的知识,不懂可以查看我以前的关于怎么自定义控件的文章,指示器就是用一个LinearLayout

package com.example.demotab;

import java.util.List;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;


/**
 * 
 * 
 * @author chaoyue
 *
 */
public class ViewPagerIndicator extends LinearLayout {
	/**
	 * 绘制三角形的画笔
	 */
	private Paint mPaint;
	/**
	 * path构成一个三角形
	 */
	private Path mPath;
	/**
	 * 三角形的宽度
	 */
	private int mTriangleWidth;
	/**
	 * 三角形的高度
	 */
	private int mTriangleHeight;

	/**
	 * 三角形的宽度为单个Tab的1/6
	 */
	private static final float RADIO_TRIANGEL = 1.0f / 6;
	/**
	 * 三角形的最大宽度
	 */
	private final int DIMENSION_TRIANGEL_WIDTH = (int) (getScreenWidth() / 3 * RADIO_TRIANGEL);

	/**
	 * 初始时,三角形指示器的偏移量
	 */
	private int mInitTranslationX;
	/**
	 * 手指滑动时的偏移量
	 */
	private float mTranslationX;


	/**
	 * tab上的内容
	 */
	private List<View> mTabTitles;
	/**
	 * 与之绑定的ViewPager
	 */
	public ViewPager mViewPager;

	/**
	 * 子view都为正方形宽度
	 */
	private int childViewWidth;

	/**
	 * 绑定的HorizontalScrollView
	 */
	private HorizontalScrollView horizontalScrollView;

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

	public ViewPagerIndicator(Context context, AttributeSet attrs) {
		super(context, attrs);


		// 初始化画笔
		mPaint = new Paint();
		mPaint.setAntiAlias(true);
		mPaint.setColor(Color.parseColor("#ffffffff"));
		mPaint.setStyle(Style.FILL);
		mPaint.setPathEffect(new CornerPathEffect(3));

		if (childViewWidth == 0) {
			childViewWidth = 300;
		}
	}

	public void setChildViewWidth(int childViewWidth) {
		this.childViewWidth = childViewWidth;
	}

	public void setHorizontalScrollView(
			HorizontalScrollView horizontalScrollView) {
		this.horizontalScrollView = horizontalScrollView;
	}

	/**
	 * 绘制指示器
	 */
	@Override
	protected void dispatchDraw(Canvas canvas) {
		canvas.save();
		// 画笔平移到正确的位置
		canvas.translate(mInitTranslationX + mTranslationX, getHeight() + 1);
		canvas.drawPath(mPath, mPaint);
		canvas.restore();

		super.dispatchDraw(canvas);
	}

	/**
	 * 初始化三角形的宽度
	 */
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		mTriangleWidth = (int) (w / 4 * RADIO_TRIANGEL);// 1/6 of
																		// width
		mTriangleWidth = Math.min(DIMENSION_TRIANGEL_WIDTH, mTriangleWidth);

		// 初始化三角形
		initTriangle();

		// 初始时的偏移量
		mInitTranslationX = childViewWidth / 2 - mTriangleWidth / 2;
	}

	

	/**
	 * 设置tab的标题内容 可选,可以自己在布局文件中写死
	 * 
	 * @param datas
	 */
	public void setTabItemTitles(List<View> datas) {
		// 如果传入的list有值,则移除布局文件中设置的view
		if (datas != null && datas.size() > 0) {
			this.removeAllViews();
			this.mTabTitles = datas;
			for (int i = 0; i < datas.size(); i++) {

				addView(datas.get(i), childViewWidth, childViewWidth);

			}
			// 设置item的click事件
			setItemClickEvent();
		}

	}

	/**
	 * 对外的ViewPager的回调接口
	 * 
	 * @author zhy
	 * 
	 */
	public interface PageChangeListener {
		public void onPageScrolled(int position, float positionOffset,
				int positionOffsetPixels);

		public void onPageSelected(int position);

		public void onPageScrollStateChanged(int state);
	}

	// 对外的ViewPager的回调接口
	private PageChangeListener onPageChangeListener;

	// 对外的ViewPager的回调接口的设置
	public void setOnPageChangeListener(PageChangeListener pageChangeListener) {
		this.onPageChangeListener = pageChangeListener;
	}

	// 设置关联的ViewPager
	public void setViewPager(ViewPager mViewPager, int pos) {
		this.mViewPager = mViewPager;

		mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
			@Override
			public void onPageSelected(int position) {

				// 回调
				if (onPageChangeListener != null) {
					onPageChangeListener.onPageSelected(position);
				}
			}

			@Override
			public void onPageScrolled(int position, float positionOffset,
					int positionOffsetPixels) {
				// 滚动
				scroll(position, positionOffset);

				// 回调
				if (onPageChangeListener != null) {
					onPageChangeListener.onPageScrolled(position,
							positionOffset, positionOffsetPixels);
				}

			}

			@Override
			public void onPageScrollStateChanged(int state) {
				// 回调
				if (onPageChangeListener != null) {
					onPageChangeListener.onPageScrollStateChanged(state);
				}

			}
		});
		// 设置当前页
		mViewPager.setCurrentItem(pos);
		// 高亮
	}

	/**
	 * 设置点击事件
	 */
	public void setItemClickEvent() {
		LinearLayout layout = (LinearLayout) horizontalScrollView.getChildAt(0);
		int cCount = layout.getChildCount();
		for (int i = 0; i < cCount; i++) {
			final int j = i;
			View view = layout.getChildAt(i);
			view.setOnClickListener(new OnClickListener() {
				@Override
				public void onClick(View v) {
					mViewPager.setCurrentItem(j);
				}
			});
		}
	}

	/**
	 * 初始化三角形指示器
	 */
	private void initTriangle() {
		mPath = new Path();

		mTriangleHeight = (int) (mTriangleWidth / 2 / Math.sqrt(2));
		mPath.moveTo(0, 0);
		mPath.lineTo(mTriangleWidth, 0);
		mPath.lineTo(mTriangleWidth / 2, -mTriangleHeight);
		mPath.close();
	}

	/**
	 * 指示器跟随手指滚动,以及容器滚动
	 * 
	 * @param position
	 * @param offset
	 */

	float lasthx = 0;

	public void scroll(int position, float offset) {

		int tabWidth = childViewWidth;
		int tabVisible = getWidth() / tabWidth;
		float right = tabWidth - (getWidth() - tabVisible * tabWidth);

		mTranslationX = childViewWidth * (position + offset);
		
		if (getChildCount() * tabWidth - getWidth() > 0) {
			if (offset > 0.0001f && position + 1 >= tabVisible) {
				if (position + 2 >= getChildCount() && offset > 0.0001f) {
					int x = (int) (lasthx + right * offset);
					this.scrollTo(x, 0);
					horizontalScrollView.scrollTo(x, 0);
					horizontalScrollView.invalidate();
				} else {
					int x = (int) (tabWidth * (position + 1 + offset - tabVisible));
					this.scrollTo(x, 0);
					lasthx = x;
					horizontalScrollView.scrollTo(x, 0);
					horizontalScrollView.invalidate();
				}
			}

		}
		invalidate();

	}

	/**
	 * 获得屏幕的宽度
	 * 
	 * @return
	 */
	public int getScreenWidth() {
		WindowManager wm = (WindowManager) getContext().getSystemService(
				Context.WINDOW_SERVICE);
		DisplayMetrics outMetrics = new DisplayMetrics();
		wm.getDefaultDisplay().getMetrics(outMetrics);
		return outMetrics.widthPixels;
	}

}

主要的重点是理解几个问题:初始化三角形并且移动使三角形移动的是用到Carvas

/**
	 * 初始化三角形指示器
	 */
	private void initTriangle() {
		mPath = new Path();

		mTriangleHeight = (int) (mTriangleWidth / 2 / Math.sqrt(2));
		mPath.moveTo(0, 0);
		mPath.lineTo(mTriangleWidth, 0);
		mPath.lineTo(mTriangleWidth / 2, -mTriangleHeight);
		mPath.close();
	}

/**
	 * 绘制指示器
	 */
	@Override
	protected void dispatchDraw(Canvas canvas) {
		canvas.save();
		// 画笔平移到正确的位置
		canvas.translate(mInitTranslationX + mTranslationX, getHeight() + 1);
		canvas.drawPath(mPath, mPaint);
		canvas.restore();

		super.dispatchDraw(canvas);
	}

而三角形一定的距离需要的参数就在ViewPager的移动监听OnPagerLisnear方法中,我写了个方法

/**
	 * 指示器跟随手指滚动,以及容器滚动
	 * 
	 * @param position
	 * @param offset
	 */

	float lasthx = 0;

	public void scroll(int position, float offset) {

		int tabWidth = childViewWidth;
		int tabVisible = getWidth() / tabWidth;
		float right = tabWidth - (getWidth() - tabVisible * tabWidth);

		mTranslationX = childViewWidth * (position + offset);
		
		if (getChildCount() * tabWidth - getWidth() > 0) {
			if (offset > 0.0001f && position + 1 >= tabVisible) {
				if (position + 2 >= getChildCount() && offset > 0.0001f) {
					int x = (int) (lasthx + right * offset);
					this.scrollTo(x, 0);
					horizontalScrollView.scrollTo(x, 0);
					horizontalScrollView.invalidate();
				} else {
					int x = (int) (tabWidth * (position + 1 + offset - tabVisible));
					this.scrollTo(x, 0);
					lasthx = x;
					horizontalScrollView.scrollTo(x, 0);
					horizontalScrollView.invalidate();
				}
			}

		}
		invalidate();

	}
代码中有个要注意的就是但三角形移动超出屏幕时想要再显示的话,本身这个LinearLayout一定要也要移动,上面的HorizontalScrollView也跟着移动就能做到跟内容跟tab也跟着移动的效果。

我做出的效果跟其他的都有区别就是如果tab页会超出屏幕,我是在滑到接近屏幕的最后一个时,才控制内容,tab一起移动的。

再看MainActivity

package com.example.demotab;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import android.app.Activity;
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 android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends FragmentActivity {
	private List<Fragment> mTabContents = new ArrayList<Fragment>();
	private FragmentPagerAdapter mAdapter;
	private ViewPager mViewPager;
	private List<View> mDatas = new ArrayList<View>();
	private ViewPagerIndicator mIndicator;
	private HorizontalScrollView horizontalScrollView;
	private int tabWidth;
	private LinearLayout llLayout;
	private List<String> list = Arrays.asList("栏目一", "栏目二", "栏目三", "栏目四",
			"栏目五", "栏目六", "栏目七", "栏目八");

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mViewPager = (ViewPager) findViewById(R.id.vp);
		mIndicator = (ViewPagerIndicator) findViewById(R.id.indicator);
		llLayout = (LinearLayout) findViewById(R.id.ll1);
		horizontalScrollView = (HorizontalScrollView) findViewById(R.id.hsv);
		tabWidth = this.getResources().getDimensionPixelOffset(R.dimen.text_w);
		for (int i = 0; i < list.size(); i++) {
			mDatas.add(new View(this));
			TextView textView = new TextView(this);
			textView.setText(list.get(i));
			textView.setGravity(Gravity.CENTER);
			LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
					tabWidth, LinearLayout.LayoutParams.FILL_PARENT);
			llLayout.addView(textView,layoutParams);
			mTabContents.add(MainTab.newInstance(list.get(i)));
		}
		mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
			@Override
			public int getCount() {
				return mTabContents.size();
			}

			@Override
			public Fragment getItem(int position) {
				return mTabContents.get(position);
			}
		};

		mIndicator.setChildViewWidth(tabWidth);
		mIndicator.setHorizontalScrollView(horizontalScrollView);
		mIndicator.setTabItemTitles(mDatas);
		// 设置关联的ViewPager
		mViewPager.setAdapter(mAdapter);
		mViewPager.setCurrentItem(0);
		mIndicator.setViewPager(mViewPager, 0);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
}

源码等下发出

ViewPagerIndicator 仿网易新闻tab或是知乎的发现中的tab

标签:android   网易   新闻   viewpager   canvas   

原文地址:http://blog.csdn.net/chaoyue0071/article/details/45010339

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!