Android自定义WheelView
来源:互联网 发布:淘宝保健食品认证 编辑:程序博客网 时间:2024/06/03 13:16
功能
滚轮式选择View,类似于TimePicker、DataPicker,可以设置有无边界(首尾是否相接)
效果图
说明
继承于View,以OverScroller协助完成滚动效果,使用最低API版本为9。如果有需要,可以使用Scroller代替,不影响效果。
尚未添加xml自定义属性,样式设置当前只能使用代码设置。\
部分方法说明
public void addData(String show,Object backData); //增加数据public void addData(String data); //增加数据public void setCircle(boolean isCircle); //设置是否首尾相接public void setRate(int rate); //设置滑动速度变化率public void notifyDataSetChanged(); //刷新数据及设置public void setCenterItem(int position); //设置被选中的位置(必须在数据添加后调用)public void setCenterItem(String showData); //设置被选中的数据(必须在数据添加后调用)public Object getCenterItem(); //获取当前被选中的数据public void setLineColor(int lineColor); //设置中间线条的颜色public void setTextColor(int textColor); //设置文字的颜色public void setTextSize(float textSize); //设置文字大小```使用示例
View wh= LayoutInflater.from(this).inflate(R.layout.common_window_wheel,null);
final WheelView picker= (WheelView) wh.findViewById(R.id.wheel);
picker.addData(“详情”);
picker.addData(“概要”);
picker.addData(“病历”);
picker.addData(“医嘱”);
picker.addData(“检验”);
picker.addData(“检查”);
picker.addData(“体征”);
picker.setCenterItem(4);
WPopupWindow popupWindow=new WPopupWindow(wh);
popupWindow.showAtLocation(getContentView(), Gravity.BOTTOM, 0, 0);
wh.findViewById(R.id.right).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtils.e(“nowData->”+picker.getCenterItem());
}
});
**xml代码**
**源码**
package com.newbjgoodwill.mobilecwr.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.OverScroller;
import java.util.ArrayList;
/**
* Created by zhangjianmin on 2017/12/1.
*/
public class WheelView extends View{
private float scrollY=0;
private int scrollX=0;
private int showSize=5; //展示Item的个数private float textSize=16; //文字的大小private boolean isCircle=false; //是否为环形private int rate=120; //惯性滑动比率,rate越大,速率越快private int textColor=0xFF000000; //文字颜色private int lineColor=0xFF888888; //线条的颜色private int cacheNowItem=-1; //预设当前item的位置,负数表示不设定private int currentItem=-1; //当前item位置private int width; //view的宽度private int height; //view的高度private int itemHeight; //item的高度private int itemX; //item的X位置private float centerItemTop; //中心Item的上边距位置private float centerItemBottom; //中心Item的下边距位置private float centerItemHeight; //中心Item的高度private int realHeight; //内容的实际高度private int minScrollY; //最小滚动高度private int maxScrollY; //最大滚动高度private ArrayList<HashBean> data; //数据集合private int dataSize=0;private Paint paint;private Paint coverPaint; //遮罩paintprivate Shader shader; //遮罩渐变private float lastY,downY; //上次操作的坐标以及按下时候的坐标private long downTime; //按下时的时间private OverScroller mScroller;public boolean isStart=true;public WheelView(Context context) { this(context, null);}public WheelView(Context context, AttributeSet attrs) { this(context, attrs, 0);}public WheelView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init();}private void init(){ mScroller=new OverScroller(getContext()); data=new ArrayList<>(); paint=new Paint(); paint.setAntiAlias(true); paint.setTextSize(DensityUtils.dp2px(getContext(),textSize)); paint.setTextAlign(Paint.Align.CENTER); coverPaint=new Paint(); if(showSize%2==0){ showSize+=1; }}/** * 增加数据 * @param show 显示数据 * @param backData 选中时的返回数据 */public void addData(String show,Object backData){ data.add(new HashBean(show, backData)); dataSize++;}/** * 增加数据 * @param data 显示数据和选中时的返回数据 */public void addDatas(String data,Object showPage){ addData(data, showPage);}public void clearData(){ data.clear();}public void setCircle(boolean isCircle){ this.isCircle=isCircle;}public void setTextColor(int textColor){ this.textColor=textColor; invalidate();}public void setLineColor(int lineColor){ this.lineColor=lineColor; invalidate();}public void setTextSize(float textSize){ this.textSize=textSize; paint.setTextSize(DensityUtils.dp2px(getContext(),textSize)); invalidate();}public void setRate(int rate){ this.rate=rate;}public void notifyDataSetChanged(){ isStart=true; invalidate();}private void measureData(){ if(isStart){ width=getWidth(); itemX=width/2; height=getHeight(); itemHeight=(height-getPaddingTop()-getPaddingBottom())/showSize; realHeight=dataSize*itemHeight; minScrollY=-(getRealHeight()-(showSize+1)/2*itemHeight); maxScrollY=(showSize-1)/2*itemHeight; centerItemHeight=itemHeight; centerItemTop=(height-getPaddingTop()-getPaddingBottom())/2+getPaddingTop()-centerItemHeight/2; centerItemBottom=(height-getPaddingTop()-getPaddingBottom())/2+getPaddingTop()+centerItemHeight/2; shader=new LinearGradient(0,0,0,height,new int[]{ 0xFFFFFFFF,0xAAFFFFFF,0x00FFFFFF,0x00FFFFFF,0xAAFFFFFF,0xFFFFFFFF },new float[]{ 0.0f,centerItemTop/height,centerItemTop/height,centerItemBottom/height,centerItemBottom/height,1.0f }, Shader.TileMode.REPEAT); coverPaint.setShader(shader); isStart=false; }}@Overridepublic void computeScroll() { //scroller的滚动是否完成 if(mScroller.computeScrollOffset()){ scrollY=mScroller.getCurrY(); invalidate(); } super.computeScroll();}@Overrideprotected void onDraw(Canvas canvas) { super.onDraw(canvas); measureData(); //如果设置了当前选中 if(cacheNowItem>=0){ scrollY=-(cacheNowItem-(showSize-1)/2)*itemHeight; cacheNowItem=-1; } int startItemPos=(int)-scrollY/itemHeight; //绘制的数据的起始位置 paint.setColor(textColor); for(int i=startItemPos,j=0;i<startItemPos+showSize+2;j++,i++){ float topY=j*itemHeight+scrollY%itemHeight; if(i>=0&&i<dataSize){ canvas.drawText(data.get(i).showStr,itemX, getBaseLine(paint,topY,itemHeight),paint); }else{ if(isCircle){ int pos=i%dataSize; canvas.drawText(data.get(pos<0?pos+dataSize:pos).showStr,itemX, getBaseLine(paint,topY,itemHeight),paint); } } } //绘制中间的线条和遮罩层 paint.setColor(lineColor); canvas.drawLine(getPaddingLeft(), centerItemTop, width-getPaddingRight(),centerItemTop,paint); canvas.drawLine(getPaddingLeft(), centerItemBottom, width-getPaddingRight(), centerItemBottom, paint); coverPaint.setShader(shader); canvas.drawRect(0, 0, width, height, coverPaint);}/** * 获取数据集合的大小 * @param isRefresh 是否重新计算数据集合大小 * @return */public int getDataSize(boolean isRefresh){ if(isRefresh){ dataSize=data.size(); } return data.size();}/** * 设置当前Item的位置 * @param position */public void setCenterItem(int position){ if(position>=0&&position<dataSize){ cacheNowItem=position; } invalidate();}/** * 设置选中内容 * @param showData */public void setCenterItem(String showData){ int size=data.size(); for(int i=0;i<size;i++){ if(showData.equals(data.get(i).showStr)){ cacheNowItem=i; invalidate(); return; } }}/** * 获取选中内容的数据 * * @return */public Object getCenterItem(){ if(cacheNowItem>=0){ return data.get(cacheNowItem).backData; }else{ int dy=(int)scrollY%itemHeight; //不足一个Item高度的部分 if(Math.abs(dy)>itemHeight/2){ //如果偏移大于item的一半, if(scrollY<0){ scrollY= scrollY-itemHeight-dy; }else{ scrollY=scrollY+itemHeight-dy; } }else{ scrollY=scrollY-dy; } mScroller.forceFinished(true); invalidate(); int nowChecked; if(!isCircle){ if(scrollY<minScrollY){ nowChecked=dataSize-1; }else if(scrollY>maxScrollY){ nowChecked=0; }else{ nowChecked= (int) (-scrollY/itemHeight+(showSize-1)/2); } }else{ //滚轮时,重置scrollY位置,使它出现在限定范围的等效位置 //以minScroll为相对0点,进行调整 if(scrollY<minScrollY||scrollY>=maxScrollY){ int mid= (int) ((scrollY-minScrollY)%realHeight); if(mid<0){ mid+=realHeight; } scrollY=mid+minScrollY; } nowChecked= (int) (-scrollY/itemHeight+(showSize-1)/2); } return dataSize>0?data.get(nowChecked).backData:null; }}/** * 获取选中的内容 * * @return */public Object getCenterData(){ if(cacheNowItem>=0){ return data.get(cacheNowItem).showStr; }else{ int dy=(int)scrollY%itemHeight; //不足一个Item高度的部分 if(Math.abs(dy)>itemHeight/2){ //如果偏移大于item的一半, if(scrollY<0){ scrollY= scrollY-itemHeight-dy; }else{ scrollY=scrollY+itemHeight-dy; } }else{ scrollY=scrollY-dy; } mScroller.forceFinished(true); invalidate(); int nowChecked; if(!isCircle){ if(scrollY<minScrollY){ nowChecked=dataSize-1; }else if(scrollY>maxScrollY){ nowChecked=0; }else{ nowChecked= (int) (-scrollY/itemHeight+(showSize-1)/2); } }else{ //滚轮时,重置scrollY位置,使它出现在限定范围的等效位置 //以minScroll为相对0点,进行调整 if(scrollY<minScrollY||scrollY>=maxScrollY){ int mid= (int) ((scrollY-minScrollY)%realHeight); if(mid<0){ mid+=realHeight; } scrollY=mid+minScrollY; } nowChecked= (int) (-scrollY/itemHeight+(showSize-1)/2); } return dataSize>0?data.get(nowChecked).showStr:null; }}@Overridepublic boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: downTime=System.currentTimeMillis(); downY=event.getRawY(); lastY=downY; break; case MotionEvent.ACTION_MOVE: float y=event.getRawY(); float dy=y-lastY; pretendScrollY(dy); lastY=y; break; case MotionEvent.ACTION_UP: checkStateAndPosition(); invalidate(); break; } return true;}private int getRealHeight(){ if(realHeight==0){ realHeight=dataSize*itemHeight; } return realHeight;}private void checkStateAndPosition(){ //上拉超出 if(!isCircle&&scrollY<-(getRealHeight()-(showSize+1)/2*itemHeight)){ mScroller.startScroll(0, (int)scrollY, 0, (showSize+1)/2*itemHeight-getRealHeight() - (int)scrollY,400);
// mScroller.springBack(0,(int)scrollY,0,0,minScrollY,maxScrollY);
}else if(!isCircle&&scrollY>(showSize-1)/2*itemHeight){ //下拉超出
mScroller.startScroll(0, (int) scrollY, 0, (showSize - 1) / 2 * itemHeight - (int) scrollY, 400);
// mScroller.springBack(0,(int)scrollY,0,0,minScrollY,maxScrollY);
}else{
long endTime=System.currentTimeMillis();
//超出滑动时间或者不足滑动距离
if(endTime-downTime>250||Math.abs(lastY-downY)
**WPopupWindow的源码**
package com.newbjgoodwill.mobilecwr.view;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.PopupWindow;
/**
* Created by zhangjianmin on 2017/12/1.
*/
public class WPopupWindow extends PopupWindow {
private Context context;
private boolean isBgAlpha=true;
private float alpha=0.5f;
public WPopupWindow(View contentView) { this(contentView, ViewGroup.LayoutParams.MATCH_PARENT , ViewGroup.LayoutParams.WRAP_CONTENT);}public WPopupWindow(Context context) { this(context,null);}public WPopupWindow(int width, int height) { this(null,width, height);}public WPopupWindow(Context context, AttributeSet attrs) { this(context, attrs, 0);}public WPopupWindow(View contentView, int width, int height) { this(contentView, width, height, false);}public WPopupWindow(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0);}@TargetApi(Build.VERSION_CODES.HONEYCOMB)public WPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); this.context=context; init();}public WPopupWindow(View contentView, int width, int height, boolean focusable) { super(contentView, width, height, focusable); this.context=contentView.getContext(); init();}public void setBgAlpha(boolean isAlpha,float alpha){ this.isBgAlpha=isAlpha; this.alpha=alpha;}@Overridepublic void showAsDropDown(View anchor) { this.showAsDropDown(anchor,0,0);}@Overridepublic void showAsDropDown(View anchor, int xoff, int yoff) { this.showAsDropDown(anchor, xoff, yoff, Gravity.TOP | Gravity.START);}@Overridepublic void showAsDropDown(View anchor, int xoff, int yoff, int gravity) { setWindowFilter(isBgAlpha, alpha); super.showAsDropDown(anchor, xoff, yoff, gravity);}@Overridepublic void showAtLocation(View parent, int gravity, int x, int y) { setWindowFilter(isBgAlpha, alpha); super.showAtLocation(parent, gravity, x, y);}public void init(){ setOnDismissListener(new OnDismissListener() { @Override public void onDismiss() { setWindowFilter(isBgAlpha, 1f); } }); setFocusable(true); setTouchable(true); setOutsideTouchable(true); setOutTouchCancel(false);}/** * @param isCancel 点击对话框外时,是否取消对话框 */public void setOutTouchCancel(boolean isCancel){ if(isCancel){ setBackgroundDrawable(new BitmapDrawable()); setTouchInterceptor(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (MotionEvent.ACTION_OUTSIDE == event.getAction()) { dismiss(); return true; } return false; } }); }else{ setBackgroundDrawable(null); setTouchInterceptor(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return false; } }); }}public void setWindowFilter(boolean isBgAlpha,float alpha) { if (isBgAlpha) { WindowManager.LayoutParams lp = ((Activity) context).getWindow().getAttributes(); lp.alpha = alpha; //保证华为honor颜色变暗 lp.dimAmount=alpha; ((Activity) context).getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND, WindowManager.LayoutParams.FLAG_BLUR_BEHIND); ((Activity) context).getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); //////////////////////////////////////////////////// ((Activity) context).getWindow().setAttributes(lp); }}
}
**DensityUtils 源码**
package com.newbjgoodwill.mobilecwr.view;
import android.content.Context;
import android.util.TypedValue;
/**
* Created by zhangjianmin on 2017/12/1.
*/
public class DensityUtils {
private DensityUtils() {
/* cannot be instantiated */
throw new UnsupportedOperationException(“cannot be instantiated”);
}
/** * dp转px * * @param context * @param val * @return */public static float dp2px(Context context, float dpVal) { return (float) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources().getDisplayMetrics());}/** * sp转px * * @param context * @param val * @return */public static int sp2px(Context context, float spVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, context.getResources().getDisplayMetrics());}/** * px转dp * * @param context * @param pxVal * @return */public static float px2dp(Context context, float pxVal) { final float scale = context.getResources().getDisplayMetrics().density; return (pxVal / scale);}/** * px转sp * * @param fontScale * @param pxVal * @return */public static float px2sp(Context context, float pxVal) { return (pxVal / context.getResources().getDisplayMetrics().scaledDensity);}
}
“`
- android WheelView自定义
- Android 自定义WheelView
- Android 自定义控件WheelView
- Android自定义WheelView
- Android 自定义WheelView
- Android自定义WheelView
- Android WheelView自定义续更
- Android 自定义WheelView,时钟等自定义
- Android 仿iOS时间选择器自定义WheelView
- Android自定义实现循环滚轮控件WheelView
- android 自定义实现滚动View:WheelView
- Android中自定义滑动选中控件WheelView
- Android 实现自定义的WheelView选择器
- wheelView自定义android日期时间选择器
- android wheelView
- Android 仿 Iphone 自定义滚条视图(wheelview)
- Android自定义图文混合滚动控件(基于WheelView修改)
- Android基于wheelView的自定义日期选择器(可拓展样式)
- HTTP 缓存机制
- spring入门概况和使用方法(一)
- CapsuleNet解读
- Codeforces Eyad and Math Gym
- CentOS 7.0安装flume
- Android自定义WheelView
- eclipse中maven的配置
- Linux核心再修补了安全漏洞 DirtyCOW修补不完全
- FFmpeg框架解析及核心数据结构
- REDIS系列之底层数据结构
- 下拉框的应用
- jsp与servlet的区别
- GOPS全球运维大会2018深圳站4月召开!
- tensorflow 一元线性回归