Android自訂可迴圈的滾動選取器CycleWheelView_Android

來源:互聯網
上載者:User

最近碰到個項目要使用到滾動選取器,原生的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>

以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援雲棲社區。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.