当前位置: 移动技术网 > 移动技术>移动开发>Android > Android自定义可循环的滚动选择器CycleWheelView

Android自定义可循环的滚动选择器CycleWheelView

2019年07月24日  | 移动技术网移动技术  | 我要评论

最近碰到个项目要使用到滚动选择器,原生的numberpicker可定制性太差,不大符合ui要求。
网上开源的wheelview是用scrollview写的,不能循环滚动,而且当数据量很大时要加载的item太多,性能非常低。
然后,还是自己写一个比较靠谱,用的是listview实现的。写完自己体验了一下,性能不错,再大的数据也不怕了。
感觉不错,重新封装了一下,提供了一些接口可以直接按照自己的需求定制,调用方法在mainactivity中。
补个图片: 

不多说了,直接上代码:

cyclewheelview.java:

/**
 * copyright (c) 2015
 *
 * cyclewheelview.java
 *
 * description: 
 *
 * author: liao longhui 
 *
 * ver 1.0, 2015-07-15, liao longhui, create file
 */

package com.example.wheelviewdemo;

import android.content.context;
import android.graphics.canvas;
import android.graphics.color;
import android.graphics.colorfilter;
import android.graphics.paint;
import android.graphics.drawable.drawable;
import android.os.handler;
import android.util.attributeset;
import android.view.layoutinflater;
import android.view.view;
import android.view.viewgroup;
import android.widget.abslistview;
import android.widget.baseadapter;
import android.widget.listview;
import android.widget.textview;

import java.util.arraylist;
import java.util.list;

/**
 * 可循环滚动的选择器
 * @author liao longhui
 *
 */
public class cyclewheelview extends listview {

 public static final string tag = cyclewheelview.class.getsimplename();
 private static final int color_divider_defalut = color.parsecolor("#747474");
 private static final int height_divider_default = 2;
 private static final int color_solid_default = color.parsecolor("#3e4043");
 private static final int color_solid_selet_default = color.parsecolor("#323335");
 private static final int wheel_size_default = 3;

 private handler mhandler;

 private cyclewheelviewadapter madapter;

 /**
  * labels
  */
 private list<string> mlabels;

 /**
  * color of selected label
  */
 private int mlabelselectcolor = color.white;

 /**
  * color of unselected label
  */
 private int mlabelcolor = color.gray;

 /**
  * gradual alph
  */
 private float malphagradual = 0.7f;

 /**
  * color of divider
  */
 private int dividercolor = color_divider_defalut;

 /**
  * height of divider
  */
 private int dividerheight = height_divider_default;

 /**
  * color of selected solid
  */
 private int seletedsolidcolor = color_solid_selet_default;

 /**
  * color of unselected solid
  */
 private int solidcolor = color_solid_default;

 /**
  * size of wheel , it should be odd number like 3 or greater
  */
 private int mwheelsize = wheel_size_default;

 /**
  * res id of wheel item layout
  */
 private int mitemlayoutid;

 /**
  * res id of label textview
  */
 private int mitemlabeltvid;

 /**
  * height of wheel item
  */
 private int mitemheight;

 private boolean cylceenable;

 private int mcurrentpositon;

 private wheelitemselectedlistener mitemselectedlistener;

 public cyclewheelview(context context, attributeset attrs, int defstyle) {
  super(context, attrs, defstyle);
 }

 public cyclewheelview(context context, attributeset attrs) {
  super(context, attrs);
  init();
 }

 public cyclewheelview(context context) {
  super(context);
 }

 private void init() {
  mhandler = new handler();
  mitemlayoutid = r.layout.item_cyclewheel;
  mitemlabeltvid = r.id.tv_label_item_wheel;
  madapter = new cyclewheelviewadapter();
  setverticalscrollbarenabled(false);
  setscrollingcacheenabled(false);
  setcachecolorhint(color.transparent);
  setfadingedgelength(0);
  setoverscrollmode(over_scroll_never);
  setdividerheight(0);
  setadapter(madapter);
  setonscrolllistener(new onscrolllistener() {
   @override
   public void onscrollstatechanged(abslistview view, int scrollstate) {
    if (scrollstate == scroll_state_idle) {
     view itemview = getchildat(0);
     if (itemview != null) {
      float deltay = itemview.gety();
      if (deltay == 0) {
       return;
      }
      if (math.abs(deltay) < mitemheight / 2) {
       smoothscrollby(getdistance(deltay), 50);
      } else {
       smoothscrollby(getdistance(mitemheight + deltay), 50);
      }
     }
    }
   }

   @override
   public void onscroll(abslistview view, int firstvisibleitem, int visibleitemcount,
     int totalitemcount) {
    refreshitems();
   }
  });
 }

 private int getdistance(float scrolldistance) {
  if (math.abs(scrolldistance) <= 2) {
   return (int) scrolldistance;
  } else if (math.abs(scrolldistance) < 12) {
   return scrolldistance > 0 ? 2 : -2;
  } else {
   return (int) (scrolldistance / 6);
  }
 }

 private void refreshitems() {
  int offset = mwheelsize / 2;
  int firstposition = getfirstvisibleposition();
  int position = 0;
  if (getchildat(0) == null) {
   return;
  }
  if (math.abs(getchildat(0).gety()) <= mitemheight / 2) {
   position = firstposition + offset;
  } else {
   position = firstposition + offset + 1;
  }
  if (position == mcurrentpositon) {
   return;
  }
  mcurrentpositon = position;
  if (mitemselectedlistener != null) {
   mitemselectedlistener.onitemselected(getselection(), getselectlabel());
  }
  resetitems(firstposition, position, offset);
 }

 private void resetitems(int firstposition, int position, int offset){
  for (int i = position - offset - 1; i < position + offset + 1; i++) {
   view itemview = getchildat(i - firstposition);
   if (itemview == null) {
    continue;
   }
   textview labeltv = (textview) itemview.findviewbyid(mitemlabeltvid);
   if (position == i) {
    labeltv.settextcolor(mlabelselectcolor);
    itemview.setalpha(1f);
   } else {
    labeltv.settextcolor(mlabelcolor);
    int delta = math.abs(i - position);
    double alpha = math.pow(malphagradual, delta);
    itemview.setalpha((float) alpha);
   }
  }
 }
 
 /**
  * 设置滚轮的刻度列表
  * 
  * @param labels
  */
 public void setlabels(list<string> labels) {
  mlabels = labels;
  madapter.setdata(mlabels);
  madapter.notifydatasetchanged();
  initview();
 }

 /**
  * 设置滚轮滚动监听
  * 
  * @param mitemselectedlistener
  */
 public void setonwheelitemselectedlistener(wheelitemselectedlistener mitemselectedlistener) {
  this.mitemselectedlistener = mitemselectedlistener;
 }

 /**
  * 获取滚轮的刻度列表
  * 
  * @return
  */
 public list<string> getlabels() {
  return mlabels;
 }

 /**
  * 设置滚轮是否为循环滚动
  * 
  * @param enable true-循环 false-单程
  */

 public void setcycleenable(boolean enable) {
  if (cylceenable != enable) {
   cylceenable = enable;
   madapter.notifydatasetchanged();
   setselection(getselection());
  }
 }

 /*
  * 滚动到指定位置
  */
 @override
 public void setselection(final int position) {
  mhandler.post(new runnable() {
   @override
   public void run() {
    cyclewheelview.super.setselection(getposition(position));
   }
  });
 }

 private int getposition(int positon) {
  if (mlabels == null || mlabels.size() == 0) {
   return 0;
  }
  if (cylceenable) {
   int d = integer.max_value / 2 / mlabels.size();
   return positon + d * mlabels.size();
  }
  return positon;
 }

 /**
  * 获取当前滚轮位置
  * 
  * @return
  */
 public int getselection() {
  if (mcurrentpositon == 0) {
   mcurrentpositon = mwheelsize / 2;
  }
  return (mcurrentpositon - mwheelsize / 2) % mlabels.size();
 }

 /**
  * 获取当前滚轮位置的刻度
  * 
  * @return
  */
 public string getselectlabel() {
  int position = getselection();
  position = position < 0 ? 0 : position;
  try {
   return mlabels.get(position);
  } catch (exception e) {
   return "";
  }
 }

 /**
  * 如果需要自定义滚轮每个item,调用此方法设置自定义item布局,自定义布局中需要一个textview来显示滚轮刻度
  * 
  * @param itemresid 布局文件id
  * @param labeltvid 刻度textview的资源id
  */
 public void setwheelitemlayout(int itemresid, int labeltvid) {
  mitemlayoutid = itemresid;
  mitemlabeltvid = labeltvid;
  madapter = new cyclewheelviewadapter();
  madapter.setdata(mlabels);
  setadapter(madapter);
  initview();
 }

 /**
  * 设置未选中刻度文字颜色
  * 
  * @param labelcolor
  */
 public void setlabelcolor(int labelcolor) {
  this.mlabelcolor = labelcolor;
  resetitems(getfirstvisibleposition(), mcurrentpositon, mwheelsize/2);
 }

 /**
  * 设置选中刻度文字颜色
  * 
  * @param labelselectcolor
  */
 public void setlabelselectcolor(int labelselectcolor) {
  this.mlabelselectcolor = labelselectcolor;
  resetitems(getfirstvisibleposition(), mcurrentpositon, mwheelsize/2);
 }

 /**
  * 设置滚轮刻度透明渐变值
  * 
  * @param alphagradual
  */
 public void setalphagradual(float alphagradual) {
  this.malphagradual = alphagradual;
  resetitems(getfirstvisibleposition(), mcurrentpositon, mwheelsize/2);
 }

 /**
  * 设置滚轮可显示的刻度数量,必须为奇数,且大于等于3
  * 
  * @param wheelsize
  * @throws cyclewheelviewexception 滚轮数量错误
  */
 public void setwheelsize(int wheelsize) throws cyclewheelviewexception {
  if (wheelsize < 3 || wheelsize % 2 != 1) {
   throw new cyclewheelviewexception("wheel size error , must be 3,5,7,9...");
  } else {
   mwheelsize = wheelsize;
   initview();
  }
 }
 
 /**
  * 设置块的颜色
  * @param unselectedsolidcolor 未选中的块的颜色
  * @param selectedsolidcolor 选中的块的颜色
  */
 public void setsolid(int unselectedsolidcolor, int selectedsolidcolor){
  this.solidcolor = unselectedsolidcolor;
  this.seletedsolidcolor = selectedsolidcolor;
  initview();
 }
 
 /**
  * 设置分割线样式
  * @param dividercolor 分割线颜色
  * @param dividerheight 分割线高度(px)
  */
 public void setdivider(int dividercolor, int dividerheight){
  this.dividercolor = dividercolor;
  this.dividerheight = dividerheight;
 }

 @suppresswarnings("deprecation")
 private void initview() {
  mitemheight = measureheight();
  viewgroup.layoutparams lp = getlayoutparams();
  lp.height = mitemheight * mwheelsize;
  madapter.setdata(mlabels);
  madapter.notifydatasetchanged();
  drawable backgroud = new drawable() {
   @override
   public void draw(canvas canvas) {
    int viewwidth = getwidth();
    paint dividerpaint = new paint();
    dividerpaint.setcolor(dividercolor);
    dividerpaint.setstrokewidth(dividerheight);
    paint seletedsolidpaint = new paint();
    seletedsolidpaint.setcolor(seletedsolidcolor);
    paint solidpaint = new paint();
    solidpaint.setcolor(solidcolor);
    canvas.drawrect(0, 0, viewwidth, mitemheight * (mwheelsize / 2), solidpaint);
    canvas.drawrect(0, mitemheight * (mwheelsize / 2 + 1), viewwidth, mitemheight
      * (mwheelsize), solidpaint);
    canvas.drawrect(0, mitemheight * (mwheelsize / 2), viewwidth, mitemheight
      * (mwheelsize / 2 + 1), seletedsolidpaint);
    canvas.drawline(0, mitemheight * (mwheelsize / 2), viewwidth, mitemheight
      * (mwheelsize / 2), dividerpaint);
    canvas.drawline(0, mitemheight * (mwheelsize / 2 + 1), viewwidth, mitemheight
      * (mwheelsize / 2 + 1), dividerpaint);
   }

   @override
   public void setalpha(int alpha) {
   }

   @override
   public void setcolorfilter(colorfilter cf) {
   }

   @override
   public int getopacity() {
    return 0;
   }
  };
  setbackgrounddrawable(backgroud);
 }

 private int measureheight() {
  view itemview = layoutinflater.from(getcontext()).inflate(mitemlayoutid, null);
  itemview.setlayoutparams(new viewgroup.layoutparams(viewgroup.layoutparams.match_parent,
    viewgroup.layoutparams.wrap_content));
  int w = view.measurespec.makemeasurespec(0, view.measurespec.unspecified);
  int h = view.measurespec.makemeasurespec(0, view.measurespec.unspecified);
  itemview.measure(w, h);
  int height = itemview.getmeasuredheight();
  // int width = view.getmeasuredwidth();
  return height;
 }

 public interface wheelitemselectedlistener {
  public void onitemselected(int position, string label);
 }

 public class cyclewheelviewexception extends exception {
  private static final long serialversionuid = 1l;

  public cyclewheelviewexception(string detailmessage) {
   super(detailmessage);
  }
 }

 public class cyclewheelviewadapter extends baseadapter {

  private list<string> mdata = new arraylist<string>();

  public void setdata(list<string> mwheellabels) {
   mdata.clear();
   mdata.addall(mwheellabels);
  }

  @override
  public int getcount() {
   if (cylceenable) {
    return integer.max_value;
   }
   return mdata.size() + mwheelsize - 1;
  }

  @override
  public object getitem(int position) {
   return "";
  }

  @override
  public long getitemid(int position) {
   return position;
  }

  @override
  public boolean isenabled(int position) {
   return false;
  }

  @override
  public view getview(int position, view convertview, viewgroup parent) {
   if (convertview == null) {
    convertview = layoutinflater.from(getcontext()).inflate(mitemlayoutid, null);
   }
   textview textview = (textview) convertview.findviewbyid(mitemlabeltvid);
   if (position < mwheelsize / 2
     || (!cylceenable && position >= mdata.size() + mwheelsize / 2)) {
    textview.settext("");
    convertview.setvisibility(view.invisible);
   } else {
    textview.settext(mdata.get((position - mwheelsize / 2) % mdata.size()));
    convertview.setvisibility(view.visible);
   }
   return convertview;
  }
 }
}

 

mainactivity.java: 

public class mainactivity extends activity {
 private cyclewheelview cyclewheelview0,cyclewheelview1, cyclewheelview2;

 @override
 protected void oncreate(bundle savedinstancestate) {
  super.oncreate(savedinstancestate);
  setcontentview(r.layout.activity_main);
  cyclewheelview0 = (cyclewheelview) findviewbyid(r.id.cyclewheelview);
  list<string> labels = new arraylist<>();
  for (int i = 0; i < 12; i++) {
   labels.add("" + i);
  }
  cyclewheelview0.setlabels(labels);
  cyclewheelview0.setalphagradual(0.5f);
  cyclewheelview0.setonwheelitemselectedlistener(new wheelitemselectedlistener() {
   @override
   public void onitemselected(int position, string label) {
    log.d("test", label);
   }
  });
  
  cyclewheelview1 = (cyclewheelview) findviewbyid(r.id.cyclewheelview1);
  list<string> labels1 = new arraylist<>();
  for (int i = 0; i < 24; i++) {
   labels1.add("" + i);
  }
  cyclewheelview1.setlabels(labels1);
  try {
   cyclewheelview1.setwheelsize(5);
  } catch (cyclewheelviewexception e) {
   e.printstacktrace();
  }
  cyclewheelview1.setselection(2);
  cyclewheelview1.setwheelitemlayout(r.layout.item_cyclewheel_custom, r.id.tv_label_item_wheel_custom);
  cyclewheelview1.setonwheelitemselectedlistener(new wheelitemselectedlistener() {
   @override
   public void onitemselected(int position, string label) {
    log.d("test", label);
   }
  });

  cyclewheelview2 = (cyclewheelview) findviewbyid(r.id.cyclewheelview2);
  list<string> labels2 = new arraylist<>();
  for (int i = 0; i < 60; i++) {
   labels2.add("" + i);
  }
  cyclewheelview2.setlabels(labels2);
  try {
   cyclewheelview2.setwheelsize(7);
  } catch (cyclewheelviewexception e) {
   e.printstacktrace();
  }
  cyclewheelview2.setcycleenable(true);
  cyclewheelview2.setselection(30);
  cyclewheelview2.setalphagradual(0.6f);
  cyclewheelview2.setdivider(color.parsecolor("#abcdef"), 2);
  cyclewheelview2.setsolid(color.white,color.white);
  cyclewheelview2.setlabelcolor(color.blue);
  cyclewheelview2.setlabelselectcolor(color.red);
  cyclewheelview2.setonwheelitemselectedlistener(new wheelitemselectedlistener() {
   @override
   public void onitemselected(int position, string label) {
    log.d("test", label);
   }
  });

 }
}

item_cyclewheel.xml: 

<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:padding="16dp"
 android:background="@android:color/transparent" >

 <textview
  android:id="@+id/tv_label_item_wheel"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:textsize="20sp"
  android:singleline="true"
  android:layout_centerhorizontal="true"
  android:layout_centervertical="true" />

</relativelayout>

item_cyclewheel_custom.xml: 

<?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:padding="16dp"
 android:background="@android:color/transparent" >

 <textview
  android:id="@+id/tv_label_item_wheel_custom"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:singleline="true"
  android:layout_alignparentleft="true"
  android:layout_centervertical="true" />

 <imageview
  android:layout_width="25dp"
  android:layout_height="25dp"
  android:layout_centervertical="true"
  android:layout_alignparentright="true"
  android:src="@drawable/ic_launcher" />

</relativelayout>

activity_main.xml: 

<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:orientation="horizontal" >

 <com.example.wheelviewdemo.cyclewheelview
  android:id="@+id/cyclewheelview"
  android:layout_width="0dp"
  android:layout_height="wrap_content"
  android:layout_weight="1" >
 </com.example.wheelviewdemo.cyclewheelview>
 
 <com.example.wheelviewdemo.cyclewheelview
  android:id="@+id/cyclewheelview1"
  android:layout_width="0dp"
  android:layout_height="wrap_content"
  android:layout_weight="1" >
 </com.example.wheelviewdemo.cyclewheelview>

 <com.example.wheelviewdemo.cyclewheelview
  android:id="@+id/cyclewheelview2"
  android:layout_width="0dp"
  android:layout_height="wrap_content"
  android:layout_weight="1" >
 </com.example.wheelviewdemo.cyclewheelview>
 
</linearlayout>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网