自定义随动indicator附带title颜色渐变
来源:互联网 发布:巨人网络 校招 编辑:程序博客网 时间:2024/06/07 09:37
1. 前言
在观看了#慕课网#hyman讲师所制作的视频《Android-自定义ViewPager指示器》之后,公司刚好遇到了类似的需求,于是自己按照hyman所讲根据自己的需求写了这个指示器,优化了一些代码和加入了简单的动画和title的点击联动部分,希望这个demo能帮到有类似需求的朋友。如果对此控件思路不太熟的朋友,可以看下这个视频讲解,本文是在此视频上做扩展说明。
《Android-自定义ViewPager指示器》链接地址:http://www.imooc.com/learn/615
2. 自定义随动指示器
先上效果图:
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
- 自定义随动indicator附带title颜色渐变
- android背景颜色渐变及改变title颜色 shape
- android背景颜色渐变及改变title颜色
- 自定义颜色渐变的TextView
- 自定义Indicator
- android只title背景色随着滚动渐变颜色
- title 是渐变色 并且状态栏颜色一致
- 自定义Activity的title的背景颜色
- 自定义UITabbarItem的title字体颜色
- android之自定义渐变颜色(一)
- android之自定义渐变颜色(二)
- android之自定义渐变颜色(二)
- 自定义带有渐变颜色的进度条
- 加载自定义dialog,设置颜色渐变效果
- 使用颜色渐变图片自定义条形ProgressBar
- 仿微信Tab颜色渐变自定义View
- Android自定义颜色渐变的拖动条
- 自定义渐进和渐变颜色的进度条
- C++对象指针数组与堆中对象数组
- 解决无限 This file is indented with tabs instead of 4 spaces
- pixhawk启动脚本分析
- Okhttp的高效使用方式
- ORACLE数据库操作基础入门
- 自定义随动indicator附带title颜色渐变
- linux溢出总结+windows aslr地址随机化绕过
- java中的多线程
- PorterDuffXfermode 正确使用方式(详解)
- Android中ListVIew高度自适应,解决ScrollView冲突问题以及Android表格
- Sublime 导出带有颜色的代码
- java 实现WebService 以及不同的调用方式
- crontab 定时任务
- 你应该知道的HTTP基础知识