// 滚轮控件的RecycleView public class WheelRecyclerView extends RecyclerView { //默认参数 private final int DEFAULT_WIDTH = Util.dp2px(160); private final int DEFAULT_ITEM_HEIGHT = Util.dp2px(50); private final int DEFAULT_SELECT_TEXT_COLOR = Color.BLACK; private final int DEFAULT_UNSELECT_TEXT_COLOR = getResources().getColor(R.color.color136); private final int DEFAULT_SELECT_TEXT_SIZE = Util.sp2px(14); private final int DEFAULT_UNSELECT_TEXT_SIZE = Util.sp2px(14); private final int DEFAULT_OFFSET = 1; private final int DEFAULT_DIVIDER_WIDTH = ViewGroup.LayoutParams.MATCH_PARENT; private final int DEFAULT_DIVIDER_HEIGHT = Util.dp2px(1); private final int DEFAULT_DIVIVER_COLOR = getResources().getColor(R.color.color136); private WheelAdapter mAdapter; private LinearLayoutManager mLayoutManager; private List<String> mDatas; //数据 private int mItemHeight; //选项高度 private int mOffset; //处于中间的item为选中,在头尾需补充 offset个空白view,可显示的item数量=2*offset+1 private int mSelectTextColor; //选中item的文本颜色 private int mUnselectTextColor; //非选中item的文本颜色 private float mSelectTextSize; //选中item的文本大小 private float mUnselectTextSize;//非选中item的文本大小 private float mDividerWidth;//分割线的宽度 private float mDividerHeight;//分割线高度 private int mDividerColor;//分割线颜色 private Paint mPaint;//绘制分割线的paint private OnSelectListener mOnSelectListener; private int mSelected; private Context mContext; public WheelRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); mContext = context; TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.WheelRecyclerView); mItemHeight = (int) ta.getDimension(R.styleable.WheelRecyclerView_itemHeight, DEFAULT_ITEM_HEIGHT); mSelectTextColor = ta.getColor(R.styleable.WheelRecyclerView_selectTextColor, DEFAULT_SELECT_TEXT_COLOR); mUnselectTextColor = ta.getColor(R.styleable.WheelRecyclerView_unselectTextColor, DEFAULT_UNSELECT_TEXT_COLOR); mSelectTextSize = ta.getDimension(R.styleable.WheelRecyclerView_selectTextSize, DEFAULT_SELECT_TEXT_SIZE); mUnselectTextSize = ta.getDimension(R.styleable.WheelRecyclerView_unselectTextSize, DEFAULT_UNSELECT_TEXT_SIZE); mOffset = ta.getInteger(R.styleable.WheelRecyclerView_wheelOffset, DEFAULT_OFFSET); mDividerWidth = ta.getDimension(R.styleable.WheelRecyclerView_dividerWidth, DEFAULT_DIVIDER_WIDTH); mDividerHeight = ta.getDimension(R.styleable.WheelRecyclerView_dividerHeight, DEFAULT_DIVIDER_HEIGHT); mDividerColor = ta.getColor(R.styleable.WheelRecyclerView_dividerColor, DEFAULT_DIVIVER_COLOR); ta.recycle(); mDatas = new ArrayList<>(); mPaint = new Paint(); mPaint.setColor(mDividerColor); mPaint.setStrokeWidth(mDividerHeight); init(); } private void init() { mLayoutManager = new LinearLayoutManager(getContext()); setLayoutManager(mLayoutManager); if (mDividerColor != Color.TRANSPARENT && mDividerHeight != 0 && mDividerWidth != 0) { addItemDecoration(new DividerItemDecoration()); } mAdapter = new WheelAdapter(); setAdapter(mAdapter); addOnScrollListener(new OnWheelScrollListener()); } @Override protected void onMeasure(int widthSpec, int heightSpec) { int width; int height; int heightSpecSize = MeasureSpec.getSize(heightSpec); int heightSpecMode = MeasureSpec.getMode(heightSpec); //当指定了控件高度时,每个item平分整个高度,控件无指定高度时,默认高度为可视item的累加高度 switch (heightSpecMode) { case MeasureSpec.EXACTLY: height = heightSpecSize; mItemHeight = height / (mOffset * 2 + 1); break; default: height = (mOffset * 2 + 1) * mItemHeight; break; } width = getDefaultSize(DEFAULT_WIDTH, widthSpec); if (mDividerWidth == DEFAULT_DIVIDER_WIDTH) { mDividerWidth = width; } setMeasuredDimension(width, height); } public void setData(List<String> datas) { if (datas == null) { return; } mDatas.clear(); mDatas.addAll(datas); mAdapter.notifyDataSetChanged(); setSelect(0); scrollTo(0,0); } public void setOnSelectListener(OnSelectListener listener) { mOnSelectListener = listener; } public void setSelect(int position) { mSelected = position; mLayoutManager.scrollToPosition(mSelected); } public int getSelected() { return mSelected; } private class WheelAdapter extends Adapter<WheelAdapter.WheelHolder> { // 动画转变线 private float mTurningLine; // Recyclerview视图 private RecyclerView mParentView; @Override public WheelHolder onCreateViewHolder(ViewGroup parent, int viewType) { mParentView = (RecyclerView) parent; // 以recyclerview中等高度水平线作为动画转变的标准 mTurningLine = 300; WheelHolder holder = new WheelHolder(LayoutInflater.from(getContext()).inflate(R.layout.item_wheel, parent, false)); holder.name.getLayoutParams().height = mItemHeight; return holder; } @Override public int getItemCount() { return mDatas.size() == 0 ? 0 : mDatas.size() + mOffset * 2; } @Override public void onBindViewHolder(WheelHolder holder, int position) { //头尾填充offset个空白view以使数据能处于中间选中状态 if (position < mOffset || position > mDatas.size() + mOffset - 1) { holder.name.setText(""); } else { holder.name.setText(mDatas.get(position - mOffset)); } } class WheelHolder extends ViewHolder { // 视图容器 private ViewGroup containerView; TextView name; // 子项顶部所在的位置(像素点) private float itemTop = Integer.MIN_VALUE; public WheelHolder(View itemView) { super(itemView); name = (TextView) itemView.findViewById(R.id.tv_name); } } } private class OnWheelScrollListener extends OnScrollListener { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_IDLE) { //当控件停止滚动时,获取可视范围第一个item的位置,滚动调整控件以使选中的item刚好处于正中间 int firstVisiblePos = mLayoutManager.findFirstVisibleItemPosition(); if (firstVisiblePos == RecyclerView.NO_POSITION) { return; } Rect rect = new Rect(); mLayoutManager.findViewByPosition(firstVisiblePos).getHitRect(rect); if (Math.abs(rect.top) > mItemHeight / 2) { smoothScrollBy(0, rect.bottom); mSelected = firstVisiblePos + 1; } else { smoothScrollBy(0, rect.top); mSelected = firstVisiblePos; } if (mOnSelectListener != null) { mOnSelectListener.onSelect(mSelected, mDatas.get(mSelected)); } } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); setSelectedItem(); } } private void setSelectedItem(){ //获取可视范围的第一个控件的位置 int firstVisiblePos = mLayoutManager.findFirstVisibleItemPosition(); if (firstVisiblePos == RecyclerView.NO_POSITION) { return; } Rect rect = new Rect(); mLayoutManager.findViewByPosition(firstVisiblePos).getHitRect(rect); //被选中item是否已经滑动超出中间区域 boolean overScroll = Math.abs(rect.top) > mItemHeight / 2 ? true : false; //更新可视范围内所有item的样式 for (int i = 0; i < 1 + mOffset * 2; i++) { TextView item; if (overScroll) { item = (TextView) mLayoutManager.findViewByPosition(firstVisiblePos + i + 1); } else { item = (TextView) mLayoutManager.findViewByPosition(firstVisiblePos + i); } if (i == mOffset) { item.setTextColor(mSelectTextColor); item.setTextSize(TypedValue.COMPLEX_UNIT_PX, mSelectTextSize); } else { item.setTextColor(mUnselectTextColor); item.setTextSize(TypedValue.COMPLEX_UNIT_PX, mUnselectTextSize); } } } private class DividerItemDecoration extends ItemDecoration { @Override public void onDrawOver(Canvas c, RecyclerView parent, State state) { //绘制分割线 float startX = getMeasuredWidth() / 2 - mDividerWidth / 2; float topY = mItemHeight * mOffset; float endX = getMeasuredWidth() / 2 + mDividerWidth / 2; float bottomY = mItemHeight * (mOffset + 1); c.drawLine(startX, topY, endX, topY, mPaint); c.drawLine(startX, bottomY, endX, bottomY, mPaint); } } public interface OnSelectListener { void onSelect(int position, String data); } }
public class CityPicker implements PopupWindow.OnDismissListener, View.OnClickListener { private OnCitySelectListener mOnCitySelectListener; private PopupWindow mPickerWindow; private View mParent; private WheelRecyclerView mProvinceWheel; private WheelRecyclerView mCityWheel; private WheelRecyclerView mCountyWheel; private Activity mContext; private List<String> provinces = new ArrayList<>(); private List<String> citys = new ArrayList<>(); private List<String> areas = new ArrayList<>(); List<Province> provinceList; List<City> listcity; List<String> listarea; public CityPicker(Activity context, View parent) { mContext = context; mParent = parent; //初始化选择器 View pickerView = LayoutInflater.from(context).inflate(R.layout.city_picker, null); mProvinceWheel = (WheelRecyclerView) pickerView.findViewById(R.id.wheel_province); mCityWheel = (WheelRecyclerView) pickerView.findViewById(R.id.wheel_city); mCountyWheel = (WheelRecyclerView) pickerView.findViewById(R.id.wheel_county); pickerView.findViewById(R.id.tv_exit).setOnClickListener(this); pickerView.findViewById(R.id.tv_ok).setOnClickListener(this); mPickerWindow = new PopupWindow(pickerView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, true); mPickerWindow.setBackgroundDrawable(new ColorDrawable(Color.WHITE)); mPickerWindow.setFocusable(true); mPickerWindow.setAnimationStyle(R.style.CityPickerAnim); mPickerWindow.setOnDismissListener(this); initData(); } /** * 初始化城市数据 */ private void initData() { provinceList=new ArrayList<>(); if (provinceList.size()>0){ provinceList.clear(); } provinceList = readFromAssets(mContext); citys.clear(); List<City> list = provinceList.get(0).getList(); citys.clear(); for (City bean : list) { citys.add(bean.getName()); mCityWheel.setData(citys); } listarea = provinceList.get(0).getList().get(0).getList(); areas.clear(); for (String str : listarea) { areas.add(str); mCountyWheel.setData(areas); } for (Province bean : provinceList) { provinces.add(bean.getName()); } mProvinceWheel.setData(provinces); mProvinceWheel.setOnSelectListener(new WheelRecyclerView.OnSelectListener() { @Override public void onSelect(int position, String data) { listcity = provinceList.get(position).getList(); if (citys.size() > 0) { citys.clear(); } for (City bean : listcity) { citys.add(bean.getName()); } mCityWheel.setData(citys); if (listcity == null || listcity.size() == 0) { if (areas.size() > 0) { areas.clear(); } mCountyWheel.setData(areas); } if (listcity.size() > 0) { listarea = listcity.get(0).getList(); if (areas.size() > 0) { areas.clear(); } for (String str : listarea) { areas.add(str); } mCountyWheel.setData(areas); } } }); mCityWheel.setOnSelectListener(new WheelRecyclerView.OnSelectListener() { @Override public void onSelect(int position, String data) { listarea = listcity.get(position).getList(); if (areas.size() > 0) { areas.clear(); } for (String str : listarea) { areas.add(str); } mCountyWheel.setData(areas); } }); } /** * 弹出Window时使背景变暗 * * @param alpha */ private void backgroundAlpha(float alpha) { Window window = mContext.getWindow(); WindowManager.LayoutParams lp = window.getAttributes(); lp.alpha = alpha; window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); window.setAttributes(lp); } public CityPicker setOnCitySelectListener(OnCitySelectListener listener) { mOnCitySelectListener = listener; return this; } public void show() { backgroundAlpha(0.5f); mPickerWindow.showAtLocation(mParent, Gravity.BOTTOM, 0, 0); } @Override public void onDismiss() { backgroundAlpha(1f); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.tv_ok: if (mOnCitySelectListener != null) { String provinceName = provinces.get(mProvinceWheel.getSelected()); String cityName = "", countyName = ""; if (citys.size() > 0) { cityName = citys.get(mCityWheel.getSelected()); } if (areas.size() > 0) { countyName = areas.get(mCountyWheel.getSelected()); } mOnCitySelectListener.onCitySelect(provinceName, cityName, countyName); mPickerWindow.dismiss(); } break; case R.id.tv_exit: mPickerWindow.dismiss(); break; } } public interface OnCitySelectListener { void onCitySelect(String province, String city, String county); }
public List<Province> readFromAssets(Context context) { List<Province> list = null; try { list = new ArrayList<>(); InputStream inputStream = context.getAssets().open("province.json"); String text = readTextFromSDcard(inputStream); inputStream.close(); JSONArray jsonArray = new JSONArray(text); for (int i = 0; i < jsonArray.length(); i++) { Province province = new Province(); JSONObject jsonObject = jsonArray.getJSONObject(i); province.setName(jsonObject.get("name") + ""); JSONArray jsonArray1 = jsonObject.getJSONArray("city"); List<City> cityList = new ArrayList<>(); for (int j = 0; j < jsonArray1.length(); j++) { JSONObject object = jsonArray1.getJSONObject(j); City city = new City(); city.setName(object.get("name") + ""); JSONArray jsonArray2 = object.getJSONArray("area"); List<String> list1 = new ArrayList<>(); for (int m = 0; m < jsonArray2.length(); m++) { String strlist = jsonArray2.getString(m); list1.add(strlist); } city.setList(list1); cityList.add(city); } province.setList(cityList); list.add(province); } } catch (IOException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } return list; } private String readTextFromSDcard(InputStream is) { StringBuffer buffer = null; try { InputStreamReader reader = new InputStreamReader(is, "utf-8"); BufferedReader bufferedReader = new BufferedReader(reader); buffer = new StringBuffer(""); String str; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); buffer.append("\n"); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return buffer.toString(); }
布局文件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical"> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/tv_exit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="start" android:background="?selectableItemBackgroundBorderless" android:gravity="center" android:padding="14dp" android:text="取消" android:textColor="@color/color136" android:textSize="16sp" /> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:background="?selectableItemBackgroundBorderless" android:gravity="center" android:padding="14dp" android:text="城市选择" android:textColor="@color/color0" android:textSize="16sp" /> <TextView android:id="@+id/tv_ok" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" android:background="?selectableItemBackgroundBorderless" android:gravity="center" android:padding="14dp" android:text="确定" android:textColor="@color/color136" android:textSize="16sp" /> </FrameLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:orientation="horizontal"> <com.wf.kdb.customview.citypickerview.WheelRecyclerView android:id="@+id/wheel_province" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" app:dividerWidth="80dp" /> <com.wf.kdb.customview.citypickerview.WheelRecyclerView android:id="@+id/wheel_city" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" app:dividerWidth="80dp" /> <com.wf.kdb.customview.citypickerview.WheelRecyclerView android:id="@+id/wheel_county" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" app:dividerWidth="80dp" /> </LinearLayout> </LinearLayout>