版权声明:本文为博主原创文章,未经博主允许不得转载。
自从上班后一直以来项目用到比较有难度的控件都上github搜,从来没想过自己写。
用多了别人的东西也觉得没什么意思能力也得不到提升,到了今年觉得一定要有自己的一套东西才行想法有啦就马上行动....
这次的准备使用最常用的控件 "ViewPager 滑动视图Tab " 而之前一直都使用 “PagerSlidingTabStrip开源控件 ” 这个比较有名效果也比较好,
今天呢就把它作为参考自己写一个 Sliding。
这里的先给出完整的控件代码
建议先看看 下面在单个方法讲解:
public class SlidingTab extends HorizontalScrollView {
private int textColor = 0x00808080;
private int textDefColor = 0x00808080;
private float textPadding = 15;
private float textSize = 10;
private float lineHeight = 10;
private int lineColor = 0x00808080;
private LinearLayout mLayout;
private ViewPager mPager;
private final PageListener mPageListener = new PageListener();
private Paint mPaint;
private int count ;
private float offset = 52;
private int lastScrollX;
private int currentPosition;
private float currentPositionOffset;
private OnTabPageChangeListener onTabPageChangeListener;
public SlidingTab(Context context) {
super(context);
}
public SlidingTab(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public SlidingTab(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(AttributeSet attrs){
DisplayMetrics dm = getResources().getDisplayMetrics();
offset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, offset,dm);
lineHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, lineHeight,dm);
textPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, textPadding,dm);
textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, textSize,dm);
TypedArray mTypedValue = getContext().obtainStyledAttributes(attrs,R.styleable.SlingTab);
textColor = mTypedValue.getColor(R.styleable.SlingTab_textColor, textColor);
textDefColor = mTypedValue.getColor(R.styleable.SlingTab_textDefColor, textDefColor);
textPadding = mTypedValue.getDimension(R.styleable.SlingTab_textPadding, textPadding);
textSize = mTypedValue.getDimension(R.styleable.SlingTab_textSize, textSize);
lineHeight = mTypedValue.getDimension(R.styleable.SlingTab_lineHeight, lineHeight);
lineColor = mTypedValue.getColor(R.styleable.SlingTab_lineColor, lineColor);
mTypedValue.recycle();
mPaint = new Paint();
mPaint.setStyle(Style.FILL);
mPaint.setAntiAlias(true);
setHorizontalScrollBarEnabled(false);
mLayout = new LinearLayout(getContext());
mLayout.setOrientation(LinearLayout.HORIZONTAL);
mLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
addView(mLayout);
}
public void setViewPager(ViewPager mPager){
if(mPager == null){
throw new NullPointerException("current ViewPager for empty");
}
this.mPager = mPager;
mPager.addOnPageChangeListener(mPageListener);
notifyDataSetChanged();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(count < 0){
return;
}
mPaint.setColor(lineColor);
int height = getMeasuredHeight();
float lineLeft = mLayout.getChildAt(currentPosition).getLeft() + textPadding;
float lineRight = mLayout.getChildAt(currentPosition).getRight()- textPadding;
if(currentPositionOffset > 0F && currentPosition < count){
float Left = mLayout.getChildAt(currentPosition + 1).getLeft() + textPadding;
float Right = mLayout.getChildAt(currentPosition + 1).getRight()- textPadding;
lineLeft = (currentPositionOffset * Left + (1F - currentPositionOffset) * lineLeft);
lineRight = (currentPositionOffset * Right + (1F - currentPositionOffset) * lineRight);
}
canvas.drawRect(lineLeft , height - lineHeight, lineRight, height, mPaint);
}
private void notifyDataSetChanged() {
count = mPager.getAdapter().getCount();
for (int i = 0; i < count; i++) {
mLayout.addView(addTab(i, mPager.getAdapter().getPageTitle(i).toString()));
}
scrollToChild(0, 0);
}
private TextView addTab(final int position, String title) {
TextView mTextView = new TextView(getContext());
mTextView.setId(position);
mTextView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
mTextView.setGravity(Gravity.CENTER);
mTextView.setText(title);
mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,textSize);
mTextView.setTextColor(textDefColor);
mTextView.setPadding((int)textPadding, 0, (int)textPadding, 0);
mTextView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mPager.setCurrentItem(position);
}
});
return mTextView;
}
void scrollToChild(int position , int positionOffset){
if(count < 0 ){
return;
}
int newPositionOffset = mLayout.getChildAt(position).getLeft() + positionOffset;
if(position > 0 || positionOffset > 0 ){
newPositionOffset -= offset;
}
if(newPositionOffset != lastScrollX){
lastScrollX = newPositionOffset;
scrollTo(newPositionOffset, 0);
}
}
public interface OnTabPageChangeListener{
void onPageScrollStateChanged(int state);
void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
void onPageSelected(int position);
}
class PageListener implements OnPageChangeListener{
@Override
public void onPageScrollStateChanged(int state) {
if(onTabPageChangeListener != null){
onTabPageChangeListener.onPageScrollStateChanged(state);
}
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
SlidingTab.this.currentPosition = position;
SlidingTab.this.currentPositionOffset = positionOffset;
scrollToChild(position, (int) (positionOffset * mLayout.getChildAt(position).getWidth()) );
invalidate();
if(onTabPageChangeListener != null){
onTabPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
@Override
public void onPageSelected(int position) {
if(onTabPageChangeListener != null){
onTabPageChangeListener.onPageSelected(position );
}
}
}
public int getTextColor() {
return textColor;
}
public void setTextColor(int textColor) {
this.textColor = textColor;
invalidate();
}
public int getTextDefColor() {
return textDefColor;
}
public void setTextDefColor(int textDefColor) {
this.textDefColor = textDefColor;
invalidate();
}
public float getTextPadding() {
return textPadding;
}
public void setTextPadding(float textPadding) {
this.textPadding = textPadding;
invalidate();
}
public float getTextSize() {
return textSize;
}
public void setTextSize(float textSize) {
this.textSize = textSize;
invalidate();
}
public float getLineHeight() {
return lineHeight;
}
public void setLineHeight(float lineHeight) {
this.lineHeight = lineHeight;
invalidate();
}
public int getLineColor() {
return lineColor;
}
public void setLineColor(int lineColor) {
this.lineColor = lineColor;
invalidate();
}
public OnTabPageChangeListener getOnTabPageChangeListener() {
return onTabPageChangeListener;
}
public void setOnTabPageChangeListener(
OnTabPageChangeListener onTabPageChangeListener) {
this.onTabPageChangeListener = onTabPageChangeListener;
}
}
要写控件首先要想的是这控件具备什么属性。
而我们这个控件的属性是:
ViewPager 每页面对应一个标题,标题就是 Text ,Text有颜色大小
ViewPager 页面对应的标识以线为基础。
//标题颜色 : 这里是为了后期的扩展
private int textColor = 0x00808080;
//默认标题颜色
private int textDefColor = 0x00808080;
// 每个标题的边距
private float textPadding = 15;
// 标题字体大小
private float textSize = 10;
// 页面标识 线的高度
private float lineHeight = 10;
当我们把控件具备的属性都了解清楚啦,并且都写出来啦,这个时候为了方便就需要自定义当前控件的标签属性,在res底下的values文件夹中创建一个attr.xml内容如下
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SlingTab">
<attr name="textColor" format="color" />
<attr name="textDefColor" format="color" />
<attr name="textPadding" format="dimension" />
<attr name="textSize" format="dimension" />
<attr name="lineHeight" format="dimension" />
<attr name="lineColor" format="color" />
</declare-styleable>
</resources>
xml创建好啦,这个时候我们就要去引用这些属性啦让它可以成为标签的属性
public SlidingTab(Context context) {
super(context);
}
public SlidingTab(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public SlidingTab(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(AttributeSet attrs){
TypedArray mTypedValue = getContext().obtainStyledAttributes(attrs,R.styleable.SlingTab);
textColor = mTypedValue.getColor(R.styleable.SlingTab_textColor, textColor);
textDefColor = mTypedValue.getColor(R.styleable.SlingTab_textDefColor, textDefColor);
textPadding = mTypedValue.getDimension(R.styleable.SlingTab_textPadding, textPadding);
textSize = mTypedValue.getDimension(R.styleable.SlingTab_textSize, textSize);
lineHeight = mTypedValue.getDimension(R.styleable.SlingTab_lineHeight, lineHeight);
lineColor = mTypedValue.getColor(R.styleable.SlingTab_lineColor, lineColor);
mTypedValue.recycle();
}
接下来就创建线的画笔,而当前控件是继承至 HorizontalScrollView只能有一个子View那么就将Linearlayout作为子布局。
mPaint = new Paint();
mPaint.setStyle(Style.FILL);
mPaint.setAntiAlias(true);
setHorizontalScrollBarEnabled(false);
mLayout = new LinearLayout(getContext());
mLayout.setOrientation(LinearLayout.HORIZONTAL);
mLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
addView(mLayout);
接着传入进来的ViewPager通过ViewPager获取标题名称以及标题个数然后创建TextView,设置ViewPager 滑动监听为了实现后期滑动的标示偏移值以及TextView实现点击事件。
public void setViewPager(ViewPager mPager){
if(mPager == null){
throw new NullPointerException("current ViewPager for empty");
}
this.mPager = mPager;
mPager.addOnPageChangeListener(mPageListener);
notifyDataSetChanged();
}
private void notifyDataSetChanged() {
count = mPager.getAdapter().getCount();
for (int i = 0; i < count; i++) {
mLayout.addView(addTab(i, mPager.getAdapter().getPageTitle(i).toString()));
}
scrollToChild(0, 0);
}
private TextView addTab(final int position, String title) {
TextView mTextView = new TextView(getContext());
mTextView.setId(position);
mTextView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
mTextView.setGravity(Gravity.CENTER);
mTextView.setText(title);
mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,textSize);
mTextView.setTextColor(textDefColor);
mTextView.setPadding((int)textPadding, 0, (int)textPadding, 0);
mTextView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mPager.setCurrentItem(position);
}
});
return mTextView;
}
到这里基本的添加都做好啦,但是标示的移动以及标题超出页面后需要滑动对应每个标题还没做,先来看看滑动怎么做,这里就需要使用ViewPager滑动监听接口中的onPagerScrolled方法,方法中的参数为:
position:滑动时,屏幕左侧显示的第一个page
positionOffset:滑动比例,值的范围为[0, 1),手指往左滑动,该值递增,反之递减
positionOffsetPixels:滑动距离,和屏幕有关,手指往左滑动,该值递增,反之递减
我们将当前position 以及 position 乘以每个标题的宽度传入scrollToChild方法中,再拿到当前position标题的左边X轴位置 + Positionoffset ,加上if语句如果当前的 position大于0或者 Positionoffset 大于0
就 -= 我们设置的偏移量 再加上if语句 如果当前 newPositionOffset 不等于lastScrollX 就将 lastScrollX = newPositionOffset;
scrollTo方法进行移动。
“注:该方法scrollTo(x,y)正数往左滑动,负数往右滑动与我们所认识的滑动是发向的 详情请看API”
class PageListener implements OnPageChangeListener{
@Override
public void onPageScrollStateChanged(int state) {
if(onTabPageChangeListener != null){
onTabPageChangeListener.onPageScrollStateChanged(state);
}
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
SlidingTab.this.currentPosition = position;
SlidingTab.this.currentPositionOffset = positionOffset;
scrollToChild(position, (int) (positionOffset * mLayout.getChildAt(position).getWidth()) );
invalidate();
if(onTabPageChangeListener != null){
onTabPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
@Override
public void onPageSelected(int position) {
if(onTabPageChangeListener != null){
onTabPageChangeListener.onPageSelected(position );
}
}
void scrollToChild(int position , int positionOffset){
if(count < 0 ){
return;
}
int newPositionOffset = mLayout.getChildAt(position).getLeft() + positionOffset;
if(position > 0 || positionOffset > 0 ){
newPositionOffset -= offset;
}
if(newPositionOffset != lastScrollX){
lastScrollX = newPositionOffset;
scrollTo(newPositionOffset, 0);
}
}
还有最后一个方法画标示线,获取测量后的View的高度,根据currentPosition 获取标题左左边以及右边左边。
判断当前currentPositionOffset 是否大于0并且当前currentPosition 小于标题个数,通过条件 根据currentPosition+1 获取标题左左边以及右边左边做下计算:
偏移值 * 第二个标题左边X坐标 + (1.0 - 偏移值) * 第一个标题的左边X坐标;
ViewPager滑动过程会不停调用
onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
方法 偏移值就会不停的变 通过invalidate();
去更新图层
,将数据画在距形上就搞定啦。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(count < 0){
return;
}
mPaint.setColor(lineColor);
int height = getMeasuredHeight();
float lineLeft = mLayout.getChildAt(currentPosition).getLeft() + textPadding;
float lineRight = mLayout.getChildAt(currentPosition).getRight()- textPadding;
if(currentPositionOffset > 0F && currentPosition < count){
float Left = mLayout.getChildAt(currentPosition + 1).getLeft() + textPadding;
float Right = mLayout.getChildAt(currentPosition + 1).getRight()- textPadding;
lineLeft = (currentPositionOffset * Left + (1F - currentPositionOffset) * lineLeft);
lineRight = (currentPositionOffset * Right + (1F - currentPositionOffset) * lineRight);
}
canvas.drawRect(lineLeft , height - lineHeight, lineRight, height, mPaint);
}
截图.jpg
最终效果图.gif
YHPKD~TKEX26L7CA1%8Z12E.jpg
如果看不太明白请原谅,第二次写博客不是表达的很好,
需要源码 留下邮箱~
网友评论