仿IOS 带字母索引的滑轮控件

举报
再见孙悟空_ 发表于 2022/01/13 01:16:55 2022/01/13
【摘要】   效果大概就是这样,右边是字母索引效果 做开发的时候,经常碰到产品经理设计出来的界面是参考IOS控件设计出来的 ,比如上图效果  ios有个控件是UIPickerView  就是可以上下滑动 并有些3d效果,非常炫。 但是android并没有提供这样的原生控件支持,所以需要通过其...

 

效果大概就是这样,右边是字母索引效果

做开发的时候,经常碰到产品经理设计出来的界面是参考IOS控件设计出来的 ,比如上图效果  ios有个控件是UIPickerView  就是可以上下滑动 并有些3d效果,非常炫。

但是android并没有提供这样的原生控件支持,所以需要通过其他方式实现类似效果。上图就是我开发中用到的一个效果。

话不多说 ,直接上代码:

Activity


  
  1. package com.example.picscrollview;
  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. import java.util.List;
  5. import com.example.picscrollview.PickerScrollView.onSelectListener;
  6. import com.example.test.R;
  7. import android.app.Activity;
  8. import android.os.Bundle;
  9. import android.view.View;
  10. import android.view.View.OnClickListener;
  11. import android.widget.Toast;
  12. public class MainActivity extends Activity implements OnClickListener {
  13. Boolean state_pressed = true;
  14. private String cities[] ={"北京版", "天津版", "上海版", "重庆版", "河北版", "山西版", "辽宁版", "吉林版", "黑龙江版", "江苏版", "浙江版", "安徽版",
  15. "福建版", "江西版", "山东版", "河南版", "湖北版", "湖南版", "广东版", "甘肃版", "四川版", "贵州版", "海南版", "云南版",
  16. "青海版", "陕西版", "广西版", "西藏版", "宁夏版", "新疆版", "内蒙古版", "澳门版", "香港版", "台湾版","全国版"};
  17. private String[] id = { "1", "2", "3", "4", "5", "6" ,"7","8","9","10","11","12","13","14","15","16",
  18. "17", "18", "19", "20", "21", "22" ,"23","24","25","26","27","28","29","30","31","32","33","34","35"};
  19. private PickerScrollView pickerscrlllview; // 滚动选择器
  20. private List<Pickers> list; // 滚动选择器数据
  21. private CharacterParser mCharacterParser;
  22. @Override
  23. protected void onCreate(Bundle savedInstanceState) {
  24. super.onCreate(savedInstanceState);
  25. setContentView(R.layout.activity_main);
  26. initView();
  27. initData();
  28. }
  29. public void initView() {
  30. LetterSideBar letterSideBar = (LetterSideBar) findViewById(R.id.cs_letter_sb);
  31. letterSideBar
  32. .setOnTouchingLetterChangedListener(new LetterSideBar.OnTouchingLetterChangedListener() {
  33. @Override
  34. public void onTouchingLetterChanged(String letter) {
  35. int jumpPos = getPositionForSection(letter.charAt(0));
  36. pickerscrlllview.setSelected(jumpPos);
  37. }
  38. });
  39. pickerscrlllview = (PickerScrollView) findViewById(R.id.pickerscrlllview);
  40. pickerscrlllview.setOnSelectListener(pickerListener);
  41. findViewById(R.id.location_confirm_btn).setOnClickListener(this);
  42. }
  43. private void initData() {
  44. mCharacterParser = CharacterParser.getInstance();
  45. list = filledData();
  46. PinyinComparator pinyinComparator = new PinyinComparator();
  47. Collections.sort(list, pinyinComparator);
  48. pickerscrlllview.setData(list);
  49. pickerscrlllview.setSelected(0);
  50. }
  51. @Override
  52. protected void onResume() {
  53. super.onResume();
  54. }
  55. public void onPause() {
  56. super.onPause();
  57. }
  58. private List<Pickers> filledData() {
  59. List<Pickers> sortList = new ArrayList<Pickers>();
  60. for (int i = 0, n = cities.length; i < n; i++) {
  61. Pickers picker = new Pickers();
  62. picker.setShowConetnt(cities[i]);
  63. picker.setShowId(id[i]);
  64. String pinyin = mCharacterParser.getSelling(cities[i]);
  65. picker.setPinyin(pinyin);
  66. String sortString = pinyin.substring(0, 1).toUpperCase();
  67. if (sortString.matches("[A-Z]")) {
  68. picker.setSortLetter(sortString.toUpperCase());
  69. } else {
  70. picker.setSortLetter("#");
  71. }
  72. sortList.add(picker);
  73. }
  74. return sortList;
  75. }
  76. public int getPositionForSection(int section) {
  77. for (int i = 0; i < list.size(); i++) {
  78. String sortStr = list.get(i).getSortLetter();
  79. char firstChar = sortStr.toUpperCase().charAt(0);
  80. if (firstChar == section) {
  81. return i;
  82. }
  83. }
  84. return -1;
  85. }
  86. onSelectListener pickerListener = new onSelectListener() {
  87. @Override
  88. public void onSelect(Pickers pickers) {
  89. Toast.makeText(MainActivity.this, pickers.getShowConetnt(), Toast.LENGTH_LONG).show();
  90. }
  91. };
  92. @Override
  93. public void onBackPressed() {
  94. super.onBackPressed();
  95. }
  96. @Override
  97. public void onClick(View v) {
  98. switch (v.getId()) {
  99. default:
  100. break;
  101. }
  102. }
  103. }

自定义滑动view


  
  1. package com.example.picscrollview;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.Timer;
  5. import java.util.TimerTask;
  6. import com.example.test.R;
  7. import android.annotation.SuppressLint;
  8. import android.content.Context;
  9. import android.graphics.Canvas;
  10. import android.graphics.Paint;
  11. import android.graphics.Paint.Align;
  12. import android.graphics.Paint.FontMetricsInt;
  13. import android.graphics.Paint.Style;
  14. import android.os.Handler;
  15. import android.os.Message;
  16. import android.util.AttributeSet;
  17. import android.view.MotionEvent;
  18. import android.view.View;
  19. /**
  20. * 自定义滚动选择器
  21. *
  22. * @author zlw
  23. *
  24. */
  25. @SuppressLint({ "HandlerLeak", "ClickableViewAccessibility" })
  26. public class PickerScrollView extends View {
  27. public static final String TAG = "PickerView";
  28. /**
  29. * text之间间距和minTextSize之比
  30. */
  31. public static final float MARGIN_ALPHA = 2.8f;
  32. /**
  33. * 自动回滚到中间的速度
  34. */
  35. public static final float SPEED = 2;
  36. private List<Pickers> mDataList;
  37. /**
  38. * 选中的位置,这个位置是mDataList的中心位置,一直不变
  39. */
  40. private int mCurrentSelected;
  41. private Paint mPaint;
  42. private float mMaxTextSize = 14;
  43. private float mMinTextSize = 6;
  44. private float mMaxTextAlpha = 255;
  45. private float mMinTextAlpha = 120;
  46. private int mColorText = 0x333333;
  47. private int mViewHeight;
  48. private int mViewWidth;
  49. private float mLastDownY;
  50. /**
  51. * 滑动的距离
  52. */
  53. private float mMoveLen = 0;
  54. private boolean isInit = false;
  55. private onSelectListener mSelectListener;
  56. private Timer timer;
  57. private MyTimerTask mTask;
  58. Handler updateHandler = new Handler() {
  59. @Override
  60. public void handleMessage(Message msg) {
  61. if (Math.abs(mMoveLen) < SPEED) {
  62. mMoveLen = 0;
  63. if (mTask != null) {
  64. mTask.cancel();
  65. mTask = null;
  66. performSelect();
  67. }
  68. } else
  69. // 这里mMoveLen / Math.abs(mMoveLen)是为了保有mMoveLen的正负号,以实现上滚或下滚
  70. mMoveLen = mMoveLen - mMoveLen / Math.abs(mMoveLen) * SPEED;
  71. invalidate();
  72. }
  73. };
  74. public PickerScrollView(Context context) {
  75. super(context);
  76. init();
  77. }
  78. public PickerScrollView(Context context, AttributeSet attrs) {
  79. super(context, attrs);
  80. init();
  81. }
  82. public void setOnSelectListener(onSelectListener listener) {
  83. mSelectListener = listener;
  84. }
  85. private void performSelect() {
  86. if (mSelectListener != null)
  87. mSelectListener.onSelect(mDataList.get(mCurrentSelected));
  88. }
  89. public void setData(List<Pickers> datas) {
  90. mDataList = datas;
  91. mCurrentSelected = datas.size() / 2;
  92. invalidate();
  93. }
  94. /**
  95. * 选择选中的item的index
  96. *
  97. * @param selected
  98. */
  99. public void setSelected(int selected) {
  100. mCurrentSelected = selected;
  101. int distance = mDataList.size() / 2 - mCurrentSelected;
  102. if (distance < 0)
  103. for (int i = 0; i < -distance; i++) {
  104. moveHeadToTail();
  105. mCurrentSelected--;
  106. }
  107. else if (distance > 0)
  108. for (int i = 0; i < distance; i++) {
  109. moveTailToHead();
  110. mCurrentSelected++;
  111. }
  112. invalidate();
  113. }
  114. /**
  115. * 选择选中的内容
  116. *
  117. * @param mSelectItem
  118. */
  119. public void setSelected(String mSelectItem) {
  120. for (int i = 0; i < mDataList.size(); i++)
  121. if (mDataList.get(i).equals(mSelectItem)) {
  122. setSelected(i);
  123. break;
  124. }
  125. }
  126. private void moveHeadToTail() {
  127. Pickers head = mDataList.get(0);
  128. mDataList.remove(0);
  129. mDataList.add(head);
  130. }
  131. private void moveTailToHead() {
  132. Pickers tail = mDataList.get(mDataList.size() - 1);
  133. mDataList.remove(mDataList.size() - 1);
  134. mDataList.add(0, tail);
  135. }
  136. @Override
  137. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  138. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  139. mViewHeight = getMeasuredHeight();
  140. mViewWidth = getMeasuredWidth();
  141. // 按照View的高度计算字体大小
  142. mMaxTextSize = mViewHeight / 8.0f;
  143. mMinTextSize = mMaxTextSize / 2f;
  144. isInit = true;
  145. invalidate();
  146. }
  147. private void init() {
  148. timer = new Timer();
  149. mDataList = new ArrayList<Pickers>();
  150. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  151. mPaint.setStyle(Style.FILL);
  152. mPaint.setTextAlign(Align.CENTER);
  153. mPaint.setColor(getResources().getColor(R.color.white));
  154. }
  155. @Override
  156. protected void onDraw(Canvas canvas) {
  157. super.onDraw(canvas);
  158. // 根据index绘制view
  159. if (isInit)
  160. drawData(canvas);
  161. }
  162. private void drawData(Canvas canvas) {
  163. // 先绘制选中的text再往上往下绘制其余的text
  164. float scale = parabola(mViewHeight / 4.0f, mMoveLen);
  165. float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize;
  166. mPaint.setTextSize(size);
  167. mPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha));
  168. // text居中绘制,注意baseline的计算才能达到居中,y值是text中心坐标
  169. float x = (float) (mViewWidth / 2.0);
  170. float y = (float) (mViewHeight / 2.0 + mMoveLen);
  171. FontMetricsInt fmi = mPaint.getFontMetricsInt();
  172. float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0));
  173. int indexs = mCurrentSelected;
  174. String textData = mDataList.get(indexs).getShowConetnt();
  175. canvas.drawText(textData, x, baseline, mPaint);
  176. // 绘制上方data
  177. for (int i = 1; (mCurrentSelected - i) >= 0; i++) {
  178. drawOtherText(canvas, i, -1);
  179. }
  180. // 绘制下方data
  181. for (int i = 1; (mCurrentSelected + i) < mDataList.size(); i++) {
  182. drawOtherText(canvas, i, 1);
  183. }
  184. }
  185. /**
  186. * @param canvas
  187. * @param position
  188. * 距离mCurrentSelected的差值
  189. * @param type
  190. * 1表示向下绘制,-1表示向上绘制
  191. */
  192. private void drawOtherText(Canvas canvas, int position, int type) {
  193. float d = (float) (MARGIN_ALPHA * mMinTextSize * position + type
  194. * mMoveLen);
  195. float scale = parabola(mViewHeight / 4.0f, d);
  196. float size = (mMaxTextSize - mMinTextSize) * scale + mMinTextSize;
  197. mPaint.setTextSize(size);
  198. mPaint.setAlpha((int) ((mMaxTextAlpha - mMinTextAlpha) * scale + mMinTextAlpha));
  199. float y = (float) (mViewHeight / 2.0 + type * d);
  200. FontMetricsInt fmi = mPaint.getFontMetricsInt();
  201. float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0));
  202. int indexs = mCurrentSelected + type * position;
  203. String textData = mDataList.get(indexs).getShowConetnt();
  204. canvas.drawText(textData, (float) (mViewWidth / 2.0), baseline, mPaint);
  205. }
  206. /**
  207. * 抛物线
  208. *
  209. * @param zero
  210. * 零点坐标
  211. * @param x
  212. * 偏移量
  213. * @return scale
  214. */
  215. private float parabola(float zero, float x) {
  216. float f = (float) (1 - Math.pow(x / zero, 2));
  217. return f < 0 ? 0 : f;
  218. }
  219. @Override
  220. public boolean onTouchEvent(MotionEvent event) {
  221. switch (event.getActionMasked()) {
  222. case MotionEvent.ACTION_DOWN:
  223. doDown(event);
  224. break;
  225. case MotionEvent.ACTION_MOVE:
  226. doMove(event);
  227. break;
  228. case MotionEvent.ACTION_UP:
  229. doUp(event);
  230. break;
  231. }
  232. return true;
  233. }
  234. private void doDown(MotionEvent event) {
  235. if (mTask != null) {
  236. mTask.cancel();
  237. mTask = null;
  238. }
  239. mLastDownY = event.getY();
  240. }
  241. private void doMove(MotionEvent event) {
  242. mMoveLen += (event.getY() - mLastDownY);
  243. if (mMoveLen > MARGIN_ALPHA * mMinTextSize / 2) {
  244. // 往下滑超过离开距离
  245. moveTailToHead();
  246. mMoveLen = mMoveLen - MARGIN_ALPHA * mMinTextSize;
  247. } else if (mMoveLen < -MARGIN_ALPHA * mMinTextSize / 2) {
  248. // 往上滑超过离开距离
  249. moveHeadToTail();
  250. mMoveLen = mMoveLen + MARGIN_ALPHA * mMinTextSize;
  251. }
  252. mLastDownY = event.getY();
  253. invalidate();
  254. }
  255. private void doUp(MotionEvent event) {
  256. // 抬起手后mCurrentSelected的位置由当前位置move到中间选中位置
  257. if (Math.abs(mMoveLen) < 0.0001) {
  258. mMoveLen = 0;
  259. return;
  260. }
  261. if (mTask != null) {
  262. mTask.cancel();
  263. mTask = null;
  264. }
  265. mTask = new MyTimerTask(updateHandler);
  266. timer.schedule(mTask, 0, 10);
  267. }
  268. class MyTimerTask extends TimerTask {
  269. Handler handler;
  270. public MyTimerTask(Handler handler) {
  271. this.handler = handler;
  272. }
  273. @Override
  274. public void run() {
  275. handler.sendMessage(handler.obtainMessage());
  276. }
  277. }
  278. public interface onSelectListener {
  279. void onSelect(Pickers pickers);
  280. }
  281. }

核心的就是上面这个类。另外右边是个字母索引条。想必大家都做过,就不一一粘贴代码了。我将这个整理出来一个demo。

大家也可以加我的qq群讨论:Android开发经验交流群 454430053。DEMO源码下载




文章来源: wukong.blog.csdn.net,作者:再见孙悟空_,版权归原作者所有,如需转载,请联系作者。

原文链接:wukong.blog.csdn.net/article/details/51720554

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。