Android自定义控件实现可左右滑动的导航条

Android自定义控件实现可左右滑动的导航条,第1张

概述先上效果图:这个控件其实算是比较轻量级的,相信不少小伙伴都能做出来。因为项目中遇到了一些特殊的定制要求,所以就自己写了一个,这里放出来。 

先上效果图:

这个控件其实算是比较轻量级的,相信不少小伙伴都能做出来。因为项目中遇到了一些特殊的定制要求,所以就自己写了一个,这里放出来。 
首先来分析下这个控件的功能: 
•能够响应左右滑动,并且能响应快速滑动
•选择项和未选择项有不同的样式表现,比如前景色,背景色,字体大小变粗之内的
•在切换选项的时候,如果当前选项未完全呈现在界面前,则自动滚动直至当前选项完全暴露显示
前两条还有,简简单单就实现了,主要是第三点,这才是我自定义这个控件的原因!那么如果要实现这个控件,需要用到哪些知识呢? 
•用Scroller来实现控件的滚动
•用VeLocityTracker来实现控件的快速滚动 

如果上面两种技术你都已经会了,那么我们就可以开始讲解代码了。首先是一些属性的Getter/Setter方法,这里采用的链式设置法: 

 public IndicatorVIEw color(int colorDefault,int colorSelected,int colorBg){  this.colorDefault = colorDefault;  this.colorSelected = colorSelected;  this.colorBg = colorBg;  return this; } public IndicatorVIEw textSize(int textSize){  this.textSize = textSize;  return this; } public IndicatorVIEw text(String[] texts){  this.texts = texts;  return this; } public IndicatorVIEw padding(int[] padding){  this.padding = padding;  return this; } public IndicatorVIEw defaultSelect(int defaultSelect){  this.selectItem = defaultSelect;  return this; } public IndicatorVIEw lineHeight(int lineHeight){  this.lineHeight = lineHeight;  return this; } public IndicatorVIEw Listener(OnIndicatorChangedListener Listener){  this.Listener = Listener;  return this; } public IndicatorVIEw type(Type type){  this.type = type;  return this; }

这里我们将每一个选项抽象成了一个Item类: 

 public class Item {  String text;  int colorDefault;  int colorSelected;  int textSize;  boolean isSelected = false;  int wIDth;  Point drawPoint;  int[] padding = new int[4];  Rect rect = new Rect(); }

 然后是控件的初始化 *** 作,主要根据当前控件的宽高,以及设置的一些属性,进行Item选项的初始化: 

 @OverrIDe protected voID onMeasure(int wIDthMeasureSpec,int heightmeasureSpec){  wIDth = MeasureSpec.getSize(wIDthMeasureSpec);  height = MeasureSpec.getSize(heightmeasureSpec);  //初始化Item  initItems();  super.onMeasure(wIDthMeasureSpec,heightmeasureSpec); } private voID initItems(){  items.clear();  measureWIDth = 0;  for(int i = 0; i < texts.length; i++){   Item item = new Item();   item.text = texts[i];   item.colorDefault = colorDefault;   item.colorSelected = colorSelected;   item.textSize = textSize;   for(int j = 0; j < item.padding.length; j++){    item.padding[j] = padding[j];   }   mPaint.setTextSize(item.textSize);   item.wIDth = (int)mPaint.measureText(item.text);   int dx = 0;   if(i - 1 < 0){    dx = 0;   }else{    for(int j = 0; j < i; j++){     dx += items.get(j).padding[0] + items.get(j).wIDth + items.get(j).padding[2];    }   }   int startX = item.padding[0] + dx;   Paint.FontMetrics metrics = mPaint.getFontMetrics();   int startY = (int)(height / 2 + (metrics.bottom - metrics.top) / 2 - metrics.bottom);   item.drawPoint = new Point(startX,startY);   //设置区域   item.rect.left = item.drawPoint.x - item.padding[0];   item.rect.top = 0;   item.rect.right = item.drawPoint.x + item.wIDth + item.padding[2];   item.rect.bottom = height;   //设置默认   if(i == selectItem){    item.isSelected = true;   }   measureWIDth += item.rect.wIDth();   items.add(item);  }  //重绘  invalIDate(); }

 接下来是事件处理,逻辑很简单。在DOWN时间记录坐标值,在MOVE中处理控件的滚动,在UP中处理滚动超屏时的恢复 *** 作,以及点击的 *** 作。 

 @OverrIDe public boolean ontouchEvent(MotionEvent event){  if(mVeLocityTracker == null) {   mVeLocityTracker = VeLocityTracker.obtain();  }  mVeLocityTracker.addMovement(event);  switch(event.getAction()){   case MotionEvent.ACTION_DOWN:    mtouchX = (int)event.getX();    mtouchY = (int)event.getY();    mMoveX = mtouchX;    return true;   case MotionEvent.ACTION_MOVE:    if(measureWIDth > wIDth){     int dx = (int)event.getX() - mMoveX;     if(dx > 0){ // 右滑      if(mScroller.getFinalX() > 0){       mScroller.startScroll(mScroller.getFinalX(),mScroller.getFinalY(),-dx,0);      }else{       mScroller.setFinalX(0);      }     }else{ //左滑      if(mScroller.getFinalX() + wIDth - dx < measureWIDth){       mScroller.startScroll(mScroller.getFinalX(),0);      }else{       mScroller.setFinalX(measureWIDth - wIDth);      }     }     mMoveX = (int)event.getX();     invalIDate();    }    break;   case MotionEvent.ACTION_UP:   case MotionEvent.ACTION_CANCEL:    if(measureWIDth > wIDth){     mVeLocityTracker.computeCurrentVeLocity(1000);     int max = Math.max(Math.abs(mScroller.getCurrX()),Math.abs(measureWIDth - wIDth - mScroller.getCurrX()));     mScroller.fling(mScroller.getFinalX(),(int)-mVeLocityTracker.getXVeLocity(),(int)-mVeLocityTracker.getYVeLocity(),max,mScroller.getFinalY());     //手指抬起时,根据滚动偏移量初始化位置     if(mScroller.getCurrX() < 0){      mScroller.abortAnimation();      mScroller.startScroll(mScroller.getCurrX(),mScroller.getCurrY(),-mScroller.getCurrX(),0);     }else if(mScroller.getCurrX() + wIDth > measureWIDth){      mScroller.abortAnimation();      mScroller.startScroll(mScroller.getCurrX(),measureWIDth - wIDth - mScroller.getCurrX(),0);     }    }    if(event.getAction() == MotionEvent.ACTION_UP){     int mUpX = (int)event.getX();     int mUpY = (int)event.getY();     //模拟点击 *** 作     if(Math.abs(mUpX - mtouchX) <= mtouchSlop && Math.abs(mUpY - mtouchY) <= mtouchSlop){      for(int i = 0; i < items.size(); i++){       if(items.get(i).rect.contains(mScroller.getCurrX() + mUpX,getScrollY() + mUpY)){        setSelected(i);        return super.ontouchEvent(event);       }      }     }    }    break;   default:    break;  }  return super.ontouchEvent(event); }

 接下来就是很重要的一段代码,因为这段代码,才可以让未完全显示的Item选项被选中时自动滚动至完全显示: 

 public voID setSelected(int @R_403_4612@){  if(@R_403_4612@ >= items.size()){   return;  }  for(int i = 0; i < items.size(); i++){   if(i == @R_403_4612@){    items.get(i).isSelected = true;    if(i != selectItem){     selectItem = i;     //判断是否需要滑动到完全可见     if(mScroller.getCurrX() + wIDth < items.get(i).rect.right){      mScroller.startScroll(mScroller.getFinalX(),items.get(i).rect.right - mScroller.getCurrX() - wIDth,mScroller.getFinalY());     }     if(items.get(i).rect.left < mScroller.getCurrX()){      mScroller.startScroll(mScroller.getFinalX(),items.get(i).rect.left - mScroller.getCurrX(),mScroller.getFinalY());     }     if(Listener != null){      Listener.onChanged(selectItem);     }    }   }else{    items.get(i).isSelected = false;   }  }  invalIDate(); }

 然后就是绘制方法了,相当于完全代理给了Item来实现: 

 @OverrIDe protected voID onDraw(Canvas canvas){  mPaint.setAntiAlias(true);  canvas.drawcolor(colorBg);  for(Item item : items){   mPaint.setTextSize(item.textSize);   if(item.isSelected){    if(type == Type.SelectByline){     //绘制红线     mPaint.setcolor(item.colorSelected);     mPaint.setStyle(Paint.Style.FILL);     canvas.drawRoundRect(new RectF(item.rect.left,item.rect.bottom - lineHeight,item.rect.right,item.rect.bottom),3,mPaint);    }else if(type == Type.SelectByFill){     //绘制红色背景     mPaint.setcolor(getContext().getResources().getcolor(androID.R.color.holo_red_light));     mPaint.setStyle(Paint.Style.FILL);     canvas.drawRoundRect(new RectF(item.rect.left + 6,item.rect.top,item.rect.right - 6,item.rect.height() * 5 / 12,mPaint);    }    mPaint.setcolor(item.colorSelected);   }else{    mPaint.setcolor(item.colorDefault);   }   canvas.drawText(item.text,item.drawPoint.x,item.drawPoint.y,mPaint);  } }

接下来就是怎么使用这个控件了,布局文件:

 <?xml version="1.0" enCoding="utf-8"?><relativeLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" androID:ID="@+ID/ListVIEw" androID:layout_wIDth="match_parent" androID:layout_height="match_parent"> <cc.wxf.androIDdemo.indicator.IndicatorVIEw  androID:ID="@+ID/indicator"  androID:layout_wIDth="match_parent"  androID:layout_height="38dp" /></relativeLayout>

MainActvity中:

package cc.wxf.androIDdemo;import androID.content.Context;import androID.content.res.Resources;import androID.os.Bundle;import androID.support.v4.app.FragmentActivity;import cc.wxf.androIDdemo.indicator.IndicatorVIEw;public class MainActivity extends FragmentActivity { private IndicatorVIEw indicatorVIEw; @OverrIDe protected voID onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentVIEw(R.layout.activity_main);  initIndicator(); } private voID initIndicator(){  indicatorVIEw = (IndicatorVIEw)findVIEwByID(R.ID.indicator);  Resources resources = getResources();  indicatorVIEw.color(resources.getcolor(androID.R.color.black),resources.getcolor(androID.R.color.holo_red_light),resources.getcolor(androID.R.color.darker_gray))    .textSize(sp2px(this,16))    .padding(new int[]{dip2px(this,14),dip2px(this,14)})    .text(new String[]{"电视剧","电影","综艺","片花","动漫","娱乐","会员1","会员2","会员3","会员4","会员5","会员6"})    .defaultSelect(0).lineHeight(dip2px(this,3))    .Listener(new IndicatorVIEw.OnIndicatorChangedListener(){     @OverrIDe     public voID onChanged(int @R_403_4612@){     }    }).commit(); } public static int dip2px(Context context,float dipValue){  final float scale = context.getResources().getdisplayMetrics().density;  return (int)(dipValue * scale + 0.5f); } public static int sp2px(Context context,float spValue){  final float scale = context.getResources().getdisplayMetrics().scaledDensity;  return (int)(spValue * scale + 0.5f); } @OverrIDe protected voID onDestroy() {  super.onDestroy();  indicatorVIEw.release(); }}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

总结

以上是内存溢出为你收集整理的Android自定义控件实现可左右滑动的导航条全部内容,希望文章能够帮你解决Android自定义控件实现可左右滑动的导航条所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1148870.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存