function
Wheeled Select View, similar to TimePicker or DataPicker, allows you to set whether or not there is a boundary (the beginning and end meet)
Design sketch
Explain
Inherited from View to assist with scrolling with OverScroller, using a minimum API version of 9.If necessary, you can use Scroller instead, without affecting the effect.
The xml custom property has not been added yet, and only code settings are currently available for style settings.\
Partial Method Description
public void addData(String show,Object backData); //Add data
public void addData(String data); //Add data
public void setCircle(boolean isCircle); //Set whether the end meets the end
public void setRate(int rate); //Set Sliding Speed Change Rate
public void notifyDataSetChanged(); //Refresh Data and Settings
public void setCenterItem(int position); //Set the selected location (must be called after data has been added)
public void setCenterItem(String showData); //Set selected data (must be called after data has been added)
public Object getCenterItem(); //Get the currently selected data
public void setLineColor(int lineColor); //Set the color of the middle line
public void setTextColor(int textColor); //Set the color of the text
public void setTextSize(float textSize); //Set Text Size```
//Use examples
View wh= LayoutInflater.from(this).inflate(R.layout.common_window_wheel,null);
final WheelView picker= (WheelView) wh.findViewById(R.id.wheel);
Picker.addData(Details);
Picker.addData(Summary);
Picker.addData(medical record);
Picker.addData(doctor's order);
Picker.addData(Test);
Picker.addData(Check);
Picker.addData(Signs);
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 Code**
**Source**
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; //Number of Item s Shown private float textSize=16; //Size of text private boolean isCircle=false; //Is it a ring private int rate=120; //Inertial sliding ratio, the larger rate, the faster rate private int textColor=0xFF000000; //Text color private int lineColor=0xFF888888; //Line color private int cacheNowItem=-1; //Preset the position of the current item, negative number means no setting private int currentItem=-1; //Current item location private int width; //Width of view private int height; //Height of view private int itemHeight; //Height of item private int itemX; //X position of item private float centerItemTop; //Top margin position of center Item private float centerItemBottom; //The lower margin position of the central Item private float centerItemHeight; //Height of center Item private int realHeight; //The actual height of the content private int minScrollY; //Minimum Roll Height private int maxScrollY; //Maximum Roll Height private ArrayList<HashBean> data; //Data Collection private int dataSize=0; private Paint paint; private Paint coverPaint; //Mask paint private Shader shader; //Mask Gradient private float lastY,downY; //Coordinates of the last operation and when pressed private long downTime; //Time when pressed 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; } } /** * Add data * @param show Display data * @param backData Return data when selected */ public void addData(String show,Object backData){ data.add(new HashBean(show, backData)); dataSize++; } /** * Add data * @param data Display data and return data when selected */ 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; } } @Override public void computeScroll() { //Is scroller scrolling complete if(mScroller.computeScrollOffset()){ scrollY=mScroller.getCurrY(); invalidate(); } super.computeScroll(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); measureData(); //If the current selection is set if(cacheNowItem>=0){ scrollY=-(cacheNowItem-(showSize-1)/2)*itemHeight; cacheNowItem=-1; } int startItemPos=(int)-scrollY/itemHeight; //Starting position of drawn data 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); } } } //Draw middle lines and masks 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); } /** * Get the size of the data collection * @param isRefresh Whether to recalculate data collection size * @return */ public int getDataSize(boolean isRefresh){ if(isRefresh){ dataSize=data.size(); } return data.size(); } /** * Set the location of the current Item * @param position */ public void setCenterItem(int position){ if(position>=0&&position<dataSize){ cacheNowItem=position; } invalidate(); } /** * Set Selection * @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; } } } /** * Get data for selection * * @return */ public Object getCenterItem(){ if(cacheNowItem>=0){ return data.get(cacheNowItem).backData; }else{ int dy=(int)scrollY%itemHeight; //Less than one Item height if(Math.abs(dy)>itemHeight/2){ //If the offset is greater than half of the 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{ //When the wheel is on, reset the scrollY position so that it appears at the equivalent position within the bounds //Adjust with minScroll as relative 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; } } /** * Get Selected * * @return */ public Object getCenterData(){ if(cacheNowItem>=0){ return data.get(cacheNowItem).showStr; }else{ int dy=(int)scrollY%itemHeight; //Less than one Item height if(Math.abs(dy)>itemHeight/2){ //If the offset is greater than half of the 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{ //When the wheel is on, reset the scrollY position so that it appears at the equivalent position within the bounds //Adjust with minScroll as relative 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; } } @Override public 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(){ //Pull up beyond 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){//drop-down exceeds
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();
//Out of sliding time or under sliding distance
if(endTime-downTime>250||Math.abs(lastY-downY)
**Source for 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; } @Override public void showAsDropDown(View anchor) { this.showAsDropDown(anchor,0,0); } @Override public void showAsDropDown(View anchor, int xoff, int yoff) { this.showAsDropDown(anchor, xoff, yoff, Gravity.TOP | Gravity.START); } @Override public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) { setWindowFilter(isBgAlpha, alpha); super.showAsDropDown(anchor, xoff, yoff, gravity); } @Override public 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 Whether to cancel the dialog when you click outside it */ 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; //Guarantee that Huawei honor darkens 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 Source**
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 To 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 To 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 To 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 Transfer to sp * * @param fontScale * @param pxVal * @return */ public static float px2sp(Context context, float pxVal) { return (pxVal / context.getResources().getDisplayMetrics().scaledDensity); }
}
"`