李晨晨:
本周我主要想实现聊天界面的RecycleView相关内容。
本次主要实现MsgRecyclerView
- public MsgRecyclerView(Context context) {
- this(context, null);
- }
- public MsgRecyclerView(Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public MsgRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init();
- }
- private void init() {
- mLayoutManager = new LinearLayoutManager(getContext());
- setLayoutManager(mLayoutManager);
- mHeadView = new MsgHeadView(getContext());
- }
- public void hideHeadView() {
- mHeadView.setVisibleHeight(0);
- isLoading = false;
- }
- @Override
- public void setAdapter(Adapter adapter) {
- mMsgViewAdapter = new MsgViewAdapter(adapter);
- super.setAdapter(mMsgViewAdapter);
- adapter.registerAdapterDataObserver(mDataObserver);
- mDataObserver.onChanged();
- }
- @Override
- public Adapter getAdapter() {
- return mMsgViewAdapter.getOriginalAdapter();
- }
因为消息列表有MsgHeadView和普通的RecycleView,所以新写了一个Adapter类,用于包含所有的内容:
- private class MsgViewAdapter extends Adapter<ViewHolder> {
- private Adapter mAdapter;
- public MsgViewAdapter(Adapter adapter) {
- this.mAdapter = adapter;
- }
- public RecyclerView.Adapter getOriginalAdapter() {
- return this.mAdapter;
- }
- @Override
- public int getItemViewType(int position) {
- if (position == 0) {
- return VIEW_TYPE_HEADER;
- }
- return mAdapter.getItemViewType(position);
- }
- @Override
- public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- if (viewType == VIEW_TYPE_HEADER) {
- return new SimpleViewHolder(mHeadView);
- }
- return mAdapter.onCreateViewHolder(parent, viewType);
- }
- @Override
- public void onBindViewHolder(ViewHolder holder, int position) {
- if (position == 0) {
- return;
- }
- mAdapter.onBindViewHolder(holder, position);
- }
- @Override
- public int getItemCount() {
- return mAdapter.getItemCount() + 1;
- }
- @Override
- public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
- mAdapter.onViewDetachedFromWindow(holder);
- }
- @Override
- public void onViewRecycled(RecyclerView.ViewHolder holder) {
- mAdapter.onViewRecycled(holder);
- }
- @Override
- public boolean onFailedToRecycleView(RecyclerView.ViewHolder holder) {
- return mAdapter.onFailedToRecycleView(holder);
- }
- @Override
- public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
- mAdapter.unregisterAdapterDataObserver(observer);
- }
- @Override
- public void registerAdapterDataObserver(AdapterDataObserver observer) {
- mAdapter.registerAdapterDataObserver(observer);
- }
- private class SimpleViewHolder extends RecyclerView.ViewHolder {
- SimpleViewHolder(View itemView) {
- super(itemView);
- }
- }
- }
2.为adapter设置观察者
自定义的DataObserver:
- private class DataObserver extends RecyclerView.AdapterDataObserver {
- @Override
- public void onChanged() {
- if (mMsgViewAdapter != null) {
- mMsgViewAdapter.notifyDataSetChanged();
- }
- }
- @Override
- public void onItemRangeInserted(int positionStart, int itemCount) {
- mMsgViewAdapter.notifyItemRangeInserted(positionStart, itemCount);
- }
- @Override
- public void onItemRangeChanged(int positionStart, int itemCount) {
- mMsgViewAdapter.notifyItemRangeChanged(positionStart, itemCount);
- }
- @Override
- public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
- mMsgViewAdapter.notifyItemRangeChanged(positionStart, itemCount, payload);
- }
- @Override
- public void onItemRangeRemoved(int positionStart, int itemCount) {
- mMsgViewAdapter.notifyItemRangeRemoved(positionStart, itemCount);
- }
- @Override
- public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
- mMsgViewAdapter.notifyItemMoved(fromPosition, toPosition);
- }
- }
3.下拉刷新相关的view显示:
- @Override
- public boolean onTouchEvent(MotionEvent e) {
- switch (e.getAction()) {
- case MotionEvent.ACTION_DOWN:
- // 记录手指按下时坐标
- mLastY = e.getRawY();
- break;
- case MotionEvent.ACTION_MOVE:
- // 计算手指滑动距离,并更新当前 Y 值
- final float deltaY = e.getRawY() - mLastY;
- mLastY = e.getRawY();
- // 若当前处于列表最上方,且headView 当前显示高度小于完整高度2倍,则更新 headView 的显示高度
- if (mLayoutManager.findFirstCompletelyVisibleItemPosition() == 0
- && (deltaY > 0) && !isLoading
- && mHeadView.getVisibleHeight() <= mHeadView.getHeadHeight() * 2) {
- mHeadView.setVisibleHeight((int) (deltaY / OFFSET_RADIO + mHeadView.getVisibleHeight()));
- }
- break;
- case MotionEvent.ACTION_UP:
- mLastY = -1;
- // 如果 headView 显示高度大于原始高度,则刷新消息列表
- if (mLayoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
- if (!isLoading && mHeadView.getVisibleHeight() > mHeadView.getHeadHeight()) {
- if (mLoadingListener != null) {
- mLoadingListener.loadPreMessage();
- isLoading = true;
- }
- mHeadView.setVisibleHeight(mHeadView.getHeadHeight());
- }else {
- // 否则,隐藏headView
- hideHeadView();
- }
- }
- break;
- }
- return super.onTouchEvent(e);
- }
- public void setLoadingListener(OnLoadingListener loadingListener) {
- mLoadingListener = loadingListener;
- }
- public interface OnLoadingListener {
- void loadPreMessage();
- }
仝心:
这次做了一些关于登陆界面的基本布局的工作,登陆界面的功能的实现(包括注册、填写账号、密码、登陆等)将在接下来的工作中逐步实现。
首先是登陆界面的布局文件,包括背景、分别输入账号和密码的文本框、登陆按键、注册按键、:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true"
- android:background="@drawable/splash"
- android:orientation="vertical">
- <!--Android图片圆角类库,一个支持每个图片角不同半径圆角的Android ImageView-->
- <com.joooonho.SelectableRoundedImageView
- android:id="@+id/imageView"
- android:layout_width="80dp"
- android:layout_height="80dp"
- app:sriv_left_top_corner_radius="20dp"
- app:sriv_left_bottom_corner_radius="20dp"
- app:sriv_right_bottom_corner_radius="20dp"
- app:sriv_right_top_corner_radius="20dp"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="58dp"
- android:src="@mipmap/app_logo_white"/>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="50dp"
- android:layout_marginLeft="40dp"
- android:gravity="center_vertical"
- android:orientation="horizontal">
- <ImageView
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:src="@mipmap/icon_user_account" />
- <EditText
- android:id="@+id/et_user_account"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="16dp"
- android:background="@null"
- android:hint="请输入账号"
- android:digits="@string/digits"
- android:inputType="textEmailAddress"
- android:textColor="@android:color/white"
- android:textColorHint="@color/white_color"
- android:textSize="16sp" />
- </LinearLayout>
- <View
- android:layout_width="match_parent"
- android:layout_height="0.8dp"
- android:layout_marginLeft="38dp"
- android:layout_marginRight="38dp"
- android:layout_marginTop="7dp"
- android:background="@color/hint_color" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="38dp"
- android:layout_marginLeft="40dp"
- android:gravity="center_vertical"
- android:orientation="horizontal">
- <ImageView
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:src="@mipmap/icon_pass_word" />
- <EditText
- android:id="@+id/et_pass_word"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="16dp"
- android:background="@null"
- android:hint="请输入密码"
- android:digits="@string/digits"
- android:inputType="textPassword"
- android:textColor="@android:color/white"
- android:textColorHint="@color/white_color"
- android:textSize="16sp" />
- </LinearLayout>
- <View
- android:layout_width="match_parent"
- android:layout_height="0.8dp"
- android:layout_marginLeft="38dp"
- android:layout_marginRight="38dp"
- android:layout_marginTop="7dp"
- android:background="@color/hint_color" />
- <TextView
- android:id="@+id/tv_btn_login"
- android:layout_width="match_parent"
- android:layout_height="42dp"
- android:layout_gravity="center_horizontal"
- android:layout_marginLeft="38dp"
- android:layout_marginRight="38dp"
- android:layout_marginTop="46dp"
- android:gravity="center"
- android:background="@drawable/login_nol_bg"
- android:text="@string/login"
- android:textColor="#1A8ECB"
- android:textSize="16sp" />
- <TextView
- android:id="@+id/tv_btn_register"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="4dp"
- android:gravity="center"
- android:paddingTop="20dp"
- android:text="@string/register_tip"
- android:textColor="@color/white_color"
- android:textSize="14sp"/>
- <View
- android:layout_width="120dp"
- android:layout_height="0.8dp"
- android:layout_gravity="center_horizontal"
- android:background="@color/white_color" />
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginTop="6dp">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="6dp"
- android:textColor="@color/app_blue_color"
- android:text="@string/app_version"/>
- </RelativeLayout>
- </LinearLayout>
聊天通信通过借助网易云通信来实现,在gradle中添加一下代码请求这些功能
- // 基础功能 (必需)
- compile 'com.netease.nimlib:basesdk:3.3.0'
- // 音视频需要
- compile 'com.netease.nimlib:avchat:3.3.0'
- // 全文检索服务需要
- compile 'com.netease.nimlib:lucene:3.3.0'
导入在文件中导入相关的库
- import com.netease.nimlib.sdk.AbortableFuture;
- import com.netease.nimlib.sdk.NIMClient;
- import com.netease.nimlib.sdk.RequestCallback;
- import com.netease.nimlib.sdk.auth.AuthService;
- import com.netease.nimlib.sdk.auth.LoginInfo;
分配一个数组用来存储各种需要申请的权限(sd卡写的权限、相机权限、读取手机状态数据的权限、录音权限、获取粗略位置(基站服务信号)和精确位置(GPS)的权限)
- private final String[] BASIC_PERMISSIONS = new String[]{
- Manifest.permission.WRITE_EXTERNAL_STORAGE,
- Manifest.permission.CAMERA,
- Manifest.permission.READ_PHONE_STATE,
- Manifest.permission.RECORD_AUDIO,
- Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION
- };
检查、申请权限的方法
- private void initPermission(){
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
- boolean has = PermissionUtils.checkPermissions(this, BASIC_PERMISSIONS);
- if (!has){
- PermissionUtils.requestPermissions(this,PERMISSION_REQUEST_CODE,BASIC_PERMISSIONS);
- }
- }
跳转到权限设置页面返回后再次检查
- protected void onRestart() {
- super.onRestart();
- initPermission();
- }
权限授予结果回调
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
- @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if (requestCode == PERMISSION_REQUEST_CODE){
- PermissionUtils.dealPermissionResult(LoginActivity.this, permissions, grantResults,
- new PermissionUtils.RequestPermissionCallBack() {
- @Override
- public void onGrant(String... permissions) {
- }
- @Override
- public void onDenied(String... permissions) {
- }
- @Override
- public void onDeniedAndNeverAsk(String... permissions) {
- }
- });
- }
- }
Activity的OnCreate方法:
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_login);
- ButterKnife.bind(this);
- mEtPassWord.setOnTouchListener(new MyTouchListener());
- initPermission();
- }
因为日后需要保存登陆信息,所以这里自定义一个SharedPreferencesUtil通过SharedPreferences提供的java常规的Long、Int、String等类型数据的保存接口来存储登陆信息
- public static void setIntSharedPreferences(Context context, String name, String key, int value) {
- SharedPreferences sharedPreferences = context.getSharedPreferences(name, Activity.MODE_PRIVATE);
- SharedPreferences.Editor editor = sharedPreferences.edit();
- editor.putInt(key, value);
- editor.commit();
- }
- public static void setStringSharedPreferences(Context context, String name, String key, String value) {
- SharedPreferences sharedPreferences = context.getSharedPreferences(name, Activity.MODE_PRIVATE);
- SharedPreferences.Editor editor = sharedPreferences.edit();
- if (sharedPreferences.contains(key)) {
- editor.remove(key);
- editor.commit();
- }
- editor.putString(key, value);
- editor.commit();
- }
- public static int getIntSharedPreferences(Context context, String name, String key) {
- SharedPreferences sharedPreferences = context.getSharedPreferences(name, Activity.MODE_PRIVATE);
- int value = sharedPreferences.getInt(key, 0);
- return value;
- }
- public static String getStringSharedPreferences(Context context, String name, String key) {
- SharedPreferences sharedPreferences = context.getSharedPreferences(name, Activity.MODE_PRIVATE);
- String value = sharedPreferences.getString(key, "");
- return value;
- }
在日后实现登陆及其它的一些功能时会用到这个类中的方法。
张静:
本周开始写WheelView——滚轮控件
我想要实现的设置生日日期,地区的方式都是用户可在可选时间,地区列表中滚动选择
所以需用到滚轮,先写滚轮控件
(1)初始化,获取设置的属性
attr.xml
- <declare-styleable name="WheelView">
- <attr name="unitHeight" format="dimension" />
- <attr name="itemNumber" format="integer"/>
- <attr name="normalTextColor" format="color" />
- <attr name="normalTextSize" format="dimension" />
- <attr name="selectedTextColor" format="color" />
- <attr name="selectedTextSize" format="dimension" />
- <attr name="lineColor" format="color" />
- <attr name="lineHeight" format="dimension" />
- <attr name="maskHeight" format="dimension"/>
- <attr name="noEmpty" format="boolean"/>
- <attr name="isEnable" format="boolean"/>
- </declare-styleable>
- private void init(Context context, AttributeSet attrs) {
- TypedArray attribute = context.obtainStyledAttributes(attrs, R.styleable.WheelView);
- unitHeight = (int) attribute.getDimension(R.styleable.WheelView_unitHeight, unitHeight);//单元格高度
- itemNumber = attribute.getInt(R.styleable.WheelView_itemNumber, itemNumber);//显示多少个内容
- normalFont = attribute.getDimension(R.styleable.WheelView_normalTextSize, normalFont);//默认字体
- selectedFont = attribute.getDimension(R.styleable.WheelView_selectedTextSize, selectedFont);//选中时字体
- normalColor = attribute.getColor(R.styleable.WheelView_normalTextColor, normalColor);//默认字体颜色
- selectedColor = attribute.getColor(R.styleable.WheelView_selectedTextColor, selectedColor);//选中时候的字体颜色
- lineColor = attribute.getColor(R.styleable.WheelView_lineColor, lineColor);//线的默认颜色
- lineHeight = attribute.getDimension(R.styleable.WheelView_lineHeight, lineHeight);//线的默认宽度
- maskHeight = attribute.getDimension(R.styleable.WheelView_maskHeight, maskHeight);//蒙版高度
- noEmpty = attribute.getBoolean(R.styleable.WheelView_noEmpty, true);//是否允许选
- isEnable = attribute.getBoolean(R.styleable.WheelView_isEnable, true);//是否可用
- attribute.recycle();
- controlHeight = itemNumber * unitHeight;
- }
(2)初始化数据
- private void initData() {
- isClearing = true;
- itemList.clear();
- for (int i = 0; i < dataList.size(); i++) {
- ItemObject itemObject = new ItemObject();
- itemObject.id = i;
- itemObject.itemText = dataList.get(i);
- itemObject.x = 0;
- itemObject.y = i * unitHeight;
- itemList.add(itemObject);
- }
- isClearing = false;
- }
其中,ItemObject(单条内容)
- private class ItemObject {
- /**id*/
- public int id = 0;
- /*** 内容*/
- public String itemText = "";
- /*** x坐标*/
- public int x = 0;
- /*** y坐标*/
- public int y = 0;
- /*** 移动距离*/
- public int move = 0;
- /*** 字体画笔*/
- private TextPaint textPaint;
- /*** 字体范围矩形*/
- private Rect textRect;
- public ItemObject() {
- super();
- }
- /**
- * 绘制自身
- *
- * @param canvas 画板
- * @param containerWidth 容器宽度
- */
- public void drawSelf(Canvas canvas, int containerWidth) {
- if (textPaint == null) {
- textPaint = new TextPaint();
- textPaint.setAntiAlias(true);//防止锯齿
- }
- if (textRect == null)
- textRect = new Rect();
- // 判断是否被选择
- if (isSelected()) {
- textPaint.setColor(selectedColor);
- // 获取距离标准位置的距离
- float moveToSelect = moveToSelected();
- moveToSelect = moveToSelect > 0 ? moveToSelect : moveToSelect * (-1);
- // 计算当前字体大小
- float textSize = normalFont
- + ((selectedFont - normalFont) * (1.0f - moveToSelect / (float) unitHeight));
- textPaint.setTextSize(textSize);
- } else {
- textPaint.setColor(normalColor);
- textPaint.setTextSize(normalFont);
- }
- // 返回包围整个字符串的最小的一个Rect区域
- itemText = (String) TextUtils.ellipsize(itemText, textPaint, containerWidth, TextUtils.TruncateAt.END);
- textPaint.getTextBounds(itemText, 0, itemText.length(), textRect);
- // 判断是否可视
- if (!isInView())
- return;
- // 绘制内容
- canvas.drawText(itemText, x + controlWidth / 2 - textRect.width() / 2,
- y + move + unitHeight / 2 + textRect.height() / 2, textPaint);
- }
- /*** 是否在可视界面内*/
- public boolean isInView() {
- if (y + move > controlHeight || (y + move + unitHeight / 2 + textRect.height() / 2) < 0)
- return false;
- return true;
- }
- /*** 移动距离*/
- public void move(int _move) {
- this.move = _move;
- }
- /*** 设置新的坐标*/
- public void newY(int _move) {
- this.move = 0;
- this.y = y + _move;
- }
- /*** 判断是否在选择区域内*/
- public boolean isSelected() {
- if ((y + move) >= controlHeight / 2 - unitHeight / 2 + lineHeight
- && (y + move) <= controlHeight / 2 + unitHeight / 2 - lineHeight) {
- return true;
- }
- if ((y + move + unitHeight) >= controlHeight / 2 - unitHeight / 2 + lineHeight
- && (y + move + unitHeight) <= controlHeight / 2 + unitHeight / 2 - lineHeight) {
- return true;
- }
- if ((y + move) <= controlHeight / 2 - unitHeight / 2 + lineHeight
- && (y + move + unitHeight) >= controlHeight / 2 + unitHeight / 2 - lineHeight) {
- return true;
- }
- return false;
- }
- /*** 获取移动到标准位置需要的距离*/
- public float moveToSelected() {
- return (controlHeight / 2 - unitHeight / 2) - (y + move);
- }
- }
(3)绘制线条
- private void drawLine(Canvas canvas) {
- if (linePaint == null) {
- linePaint = new Paint();
- linePaint.setColor(lineColor);
- linePaint.setAntiAlias(true);
- linePaint.setStrokeWidth(lineHeight);//设置画笔宽度
- }
- canvas.drawLine(0, controlHeight / 2 - unitHeight / 2 + lineHeight,
- controlWidth, controlHeight / 2 - unitHeight / 2 + lineHeight, linePaint);
- canvas.drawLine(0, controlHeight / 2 + unitHeight / 2 - lineHeight,
- controlWidth, controlHeight / 2 + unitHeight / 2 - lineHeight, linePaint);
- }
(4)绘制列表
- private synchronized void drawList(Canvas canvas) {
- if (isClearing)
- return;
- try {
- for (ItemObject itemObject : itemList) {
- itemObject.drawSelf(canvas, getMeasuredWidth());
- }
- } catch (Exception e) {
- }
- }
(5)绘制遮盖板
- private void drawMask(Canvas canvas) {
- LinearGradient lg = new LinearGradient(0, 0, 0, maskHeight, 0x00f2f2f2,
- 0x00f2f2f2, TileMode.MIRROR);
- Paint paint = new Paint();
- paint.setShader(lg);
- canvas.drawRect(0, 0, controlWidth, maskHeight, paint);
- LinearGradient lg2 = new LinearGradient(0, controlHeight - maskHeight,
- 0, controlHeight, 0x00f2f2f2, 0x00f2f2f2, TileMode.MIRROR);
- Paint paint2 = new Paint();
- paint2.setShader(lg2);
- canvas.drawRect(0, controlHeight - maskHeight, controlWidth,
- controlHeight, paint2);
- }
(6) 触摸事件
获取纵坐标(设置时间,滚轮是纵向移动)
获得的触摸动作为:
a. ACTION_DOWN(按下)
设置为正在滑动中, 获取当下纵坐标,以及当时系统时间
b. ACTION_MOVE(移动中)
刷新移动的距离,设置滑动监听
c. ACTION_UP(抬起时)
计算出这段时间移动的距离
若滑动时间小于预先设定的短促移动时间并且滑动距离大于预先设定的短促移动距离,则继续移动一段距离
否则,移动到指定位置,停止移动(actionUp)
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (!isEnable)
- return true;
- int y = (int) event.getY();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- isScrolling = true;
- downY = (int) event.getY();
- downTime = System.currentTimeMillis();
- break;
- case MotionEvent.ACTION_MOVE:
- actionMove(y - downY);
- onSelectListener();
- break;
- case MotionEvent.ACTION_UP:
- int move = Math.abs(y - downY);
- // 判断这段时间移动的距离
- if (System.currentTimeMillis() - downTime < goonTime && move > goonDistance) {
- goonMove(y - downY);
- } else {
- actionUp(y - downY);
- noEmpty();
- isScrolling = false;
- }
- break;
- default:
- break;
- }
- return true;
- }
(7)继续移动一段距离
- private synchronized void goonMove(final int move) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- int distance = 0;
- while (distance < unitHeight * MOVE_NUMBER) {
- try {
- Thread.sleep(5);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- actionThreadMove(move > 0 ? distance : distance * (-1));
- distance += 10;
- }
- actionUp(move > 0 ? distance - 10 : distance * (-1) + 10);
- noEmpty();
- }
- }).start();
- }
(8)不能为空,必须有选择
- private void noEmpty() {
- if (!noEmpty)
- return;
- for (ItemObject item : itemList) {
- if (item.isSelected())
- return;
- }
- int move = (int) itemList.get(0).moveToSelected();
- if (move < 0) {
- defaultMove(move);
- } else {
- defaultMove((int) itemList.get(itemList.size() - 1)
- .moveToSelected());
- }
- for (ItemObject item : itemList) {
- if (item.isSelected()) {
- if (onSelectListener != null)
- onSelectListener.endSelect(item.id, item.itemText);
- break;
- }
- }
- }
(9)移动的时候,即触摸事件中,触摸动作为ACTION_MOVE时所作处理
移动触摸时滑动的距离,刷新控件
- private void actionMove(int move) {
- for (ItemObject item : itemList) {
- item.move(move);
- }
- invalidate();
- }
(10)移动,线程中调用
向handler发送刷新信息,移动
- private void actionThreadMove(int move) {
- for (ItemObject item : itemList) {
- item.move(move);
- }
- Message rMessage = new Message();
- rMessage.what = REFRESH_VIEW;
- handler.sendMessage(rMessage);
- }
(11)松开的时候
当向上滑动和向下滑动时,分别获取所需移动的距离,监听器结束选择
缓慢移动之前所获得的所需移动距离
通知刷新
- private void actionUp(int move) {
- int newMove = 0;
- if (move > 0) {
- for (int i = 0; i < itemList.size(); i++) {
- if (itemList.get(i).isSelected()) {
- newMove = (int) itemList.get(i).moveToSelected();
- if (onSelectListener != null)
- onSelectListener.endSelect(itemList.get(i).id,
- itemList.get(i).itemText);
- break;
- }
- }
- } else {
- for (int i = itemList.size() - 1; i >= 0; i--) {
- if (itemList.get(i).isSelected()) {
- newMove = (int) itemList.get(i).moveToSelected();
- if (onSelectListener != null)
- onSelectListener.endSelect(itemList.get(i).id,
- itemList.get(i).itemText);
- break;
- }
- }
- }
- for (ItemObject item : itemList) {
- item.newY(move + 0);
- }
- slowMove(newMove);
- Message rMessage = new Message();
- rMessage.what = REFRESH_VIEW;
- handler.sendMessage(rMessage);
- }
(12)缓慢移动
先判断是向上滑动还是向下滑动的(移动距离取绝对值),设置移动的速度(1)
当还需继续移动时,每移动一格,重设每一项纵坐标,通知刷新,线程睡眠2ms, 移动距离减一,直到已移动完所需距离
每一项结束监听
- private synchronized void slowMove(final int move) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- // 判断正负
- int m = move > 0 ? move : move * (-1);
- int i = move > 0 ? 1 : (-1);
- // 移动速度
- int speed = 1;
- while (true) {
- m = m - speed;
- if (m <= 0) {
- for (ItemObject item : itemList) {
- item.newY(m * i);
- }
- Message rMessage = new Message();
- rMessage.what = REFRESH_VIEW;
- handler.sendMessage(rMessage);
- try {
- Thread.sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- break;
- }
- for (ItemObject item : itemList) {
- item.newY(speed * i);
- }
- Message rMessage = new Message();
- rMessage.what = REFRESH_VIEW;
- handler.sendMessage(rMessage);
- try {
- Thread.sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- for (ItemObject item : itemList) {
- if (item.isSelected()) {
- if (onSelectListener != null)
- onSelectListener.endSelect(item.id, item.itemText);
- break;
- }
- }
- }
- }).start();
- }
(13)滑动监听
- private void onSelectListener() {
- if (onSelectListener == null)
- return;
- for (ItemObject item : itemList) {
- if (item.isSelected()) {
- onSelectListener.selecting(item.id, item.itemText);
- }
- }
- }
(14)选择监听
- public interface OnSelectListener {
- /*** 结束选择*/
- void endSelect(int id, String text);
- /*** 选中的内容*/
- void selecting(int id, String text);
- }
- }
附上完整WheelView.java
- package com.ezreal.ezchat.timeselectview;
- import android.annotation.SuppressLint;
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Canvas;
- import android.graphics.LinearGradient;
- import android.graphics.Paint;
- import android.graphics.Rect;
- import android.graphics.Shader.TileMode;
- import android.os.Handler;
- import android.os.Message;
- import android.text.TextPaint;
- import android.text.TextUtils;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.View;
- import com.ezreal.ezchat.R;
- import java.util.ArrayList;
- /**
- * Created by 张静
- */
- /*** WheelView滚轮*/
- public class WheelView extends View {
- /*** 控件宽度*/
- private float controlWidth;
- /*** 控件高度*/
- private float controlHeight;
- /*** 是否滑动中*/
- private boolean isScrolling = false;
- /*** 选择的内容*/
- private ArrayList<ItemObject> itemList = new ArrayList<>();
- /*** 设置数据*/
- private ArrayList<String> dataList = new ArrayList<>();
- /*** 按下的坐标*/
- private int downY;
- /*** 按下的时间*/
- private long downTime = 0;
- /*** 短促移动*/
- private long goonTime = 200;
- /*** 短促移动距离*/
- private int goonDistance = 100;
- /*** 画线画笔*/
- private Paint linePaint;
- /*** 线的默认颜色*/
- private int lineColor = 0xff000000;
- /*** 线的默认宽度*/
- private float lineHeight = 2f;
- /*** 默认字体*/
- private float normalFont = 14.0f;
- /*** 选中的时候字体*/
- private float selectedFont = 22.0f;
- /*** 单元格高度*/
- private int unitHeight = 50;
- /*** 显示多少个内容*/
- private int itemNumber = 7;
- /*** 默认字体颜色*/
- private int normalColor = 0xff000000;
- /*** 选中时候的字体颜色*/
- private int selectedColor = 0xffff0000;
- /*** 蒙板高度*/
- private float maskHeight = 48.0f;
- /*** 选择监听*/
- private OnSelectListener onSelectListener;
- /*** 是否可用*/
- private boolean isEnable = true;
- /*** 刷新界面*/
- private static final int REFRESH_VIEW = 0x001;
- /*** 移动距离*/
- private static final int MOVE_NUMBER = 5;
- /*** 是否允许选*/
- private boolean noEmpty = true;
- /**正在修改数据,避免ConcurrentModificationException异常*/
- private boolean isClearing = false;
- public WheelView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init(context, attrs);
- initData();
- }
- public WheelView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context, attrs);
- initData();
- }
- public WheelView(Context context) {
- super(context);
- initData();
- }
- /*** 初始化,获取设置的属性*/
- private void init(Context context, AttributeSet attrs) {
- TypedArray attribute = context.obtainStyledAttributes(attrs, R.styleable.WheelView);
- unitHeight = (int) attribute.getDimension(R.styleable.WheelView_unitHeight, unitHeight);
- itemNumber = attribute.getInt(R.styleable.WheelView_itemNumber, itemNumber);
- normalFont = attribute.getDimension(R.styleable.WheelView_normalTextSize, normalFont);
- selectedFont = attribute.getDimension(R.styleable.WheelView_selectedTextSize, selectedFont);
- normalColor = attribute.getColor(R.styleable.WheelView_normalTextColor, normalColor);
- selectedColor = attribute.getColor(R.styleable.WheelView_selectedTextColor, selectedColor);
- lineColor = attribute.getColor(R.styleable.WheelView_lineColor, lineColor);
- lineHeight = attribute.getDimension(R.styleable.WheelView_lineHeight, lineHeight);
- maskHeight = attribute.getDimension(R.styleable.WheelView_maskHeight, maskHeight);
- noEmpty = attribute.getBoolean(R.styleable.WheelView_noEmpty, true);
- isEnable = attribute.getBoolean(R.styleable.WheelView_isEnable, true);
- attribute.recycle();
- controlHeight = itemNumber * unitHeight;
- }
- /*** 初始化数据*/
- private void initData() {
- isClearing = true;
- itemList.clear();
- for (int i = 0; i < dataList.size(); i++) {
- ItemObject itemObject = new ItemObject();
- itemObject.id = i;
- itemObject.itemText = dataList.get(i);
- itemObject.x = 0;
- itemObject.y = i * unitHeight;
- itemList.add(itemObject);
- }
- isClearing = false;
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- controlWidth = getMeasuredWidth();
- if (controlWidth != 0) {
- setMeasuredDimension(getMeasuredWidth(), itemNumber * unitHeight);
- }
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- drawLine(canvas);
- drawList(canvas);
- drawMask(canvas);
- }
- /*** 绘制线条*/
- private void drawLine(Canvas canvas) {
- if (linePaint == null) {
- linePaint = new Paint();
- linePaint.setColor(lineColor);
- linePaint.setAntiAlias(true);
- linePaint.setStrokeWidth(lineHeight);
- }
- canvas.drawLine(0, controlHeight / 2 - unitHeight / 2 + lineHeight,
- controlWidth, controlHeight / 2 - unitHeight / 2 + lineHeight, linePaint);
- canvas.drawLine(0, controlHeight / 2 + unitHeight / 2 - lineHeight,
- controlWidth, controlHeight / 2 + unitHeight / 2 - lineHeight, linePaint);
- }
- private synchronized void drawList(Canvas canvas) {
- if (isClearing)
- return;
- try {
- for (ItemObject itemObject : itemList) {
- itemObject.drawSelf(canvas, getMeasuredWidth());
- }
- } catch (Exception e) {
- }
- }
- /*** 绘制遮盖板*/
- private void drawMask(Canvas canvas) {
- LinearGradient lg = new LinearGradient(0, 0, 0, maskHeight, 0x00f2f2f2,
- 0x00f2f2f2, TileMode.MIRROR);
- Paint paint = new Paint();
- paint.setShader(lg);
- canvas.drawRect(0, 0, controlWidth, maskHeight, paint);
- LinearGradient lg2 = new LinearGradient(0, controlHeight - maskHeight,
- 0, controlHeight, 0x00f2f2f2, 0x00f2f2f2, TileMode.MIRROR);
- Paint paint2 = new Paint();
- paint2.setShader(lg2);
- canvas.drawRect(0, controlHeight - maskHeight, controlWidth,
- controlHeight, paint2);
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (!isEnable)
- return true;
- int y = (int) event.getY();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- isScrolling = true;
- downY = (int) event.getY();
- downTime = System.currentTimeMillis();
- break;
- case MotionEvent.ACTION_MOVE:
- actionMove(y - downY);
- onSelectListener();
- break;
- case MotionEvent.ACTION_UP:
- int move = Math.abs(y - downY);
- // 判断这段时间移动的距离
- if (System.currentTimeMillis() - downTime < goonTime && move > goonDistance) {
- goonMove(y - downY);
- } else {
- actionUp(y - downY);
- noEmpty();
- isScrolling = false;
- }
- break;
- default:
- break;
- }
- return true;
- }
- /*** 继续移动一定距离*/
- private synchronized void goonMove(final int move) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- int distance = 0;
- while (distance < unitHeight * MOVE_NUMBER) {
- try {
- Thread.sleep(5);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- actionThreadMove(move > 0 ? distance : distance * (-1));
- distance += 10;
- }
- actionUp(move > 0 ? distance - 10 : distance * (-1) + 10);
- noEmpty();
- }
- }).start();
- }
- /*** 不能为空,必须有选项*/
- private void noEmpty() {
- if (!noEmpty)
- return;
- for (ItemObject item : itemList) {
- if (item.isSelected())
- return;
- }
- int move = (int) itemList.get(0).moveToSelected();
- if (move < 0) {
- defaultMove(move);
- } else {
- defaultMove((int) itemList.get(itemList.size() - 1)
- .moveToSelected());
- }
- for (ItemObject item : itemList) {
- if (item.isSelected()) {
- if (onSelectListener != null)
- onSelectListener.endSelect(item.id, item.itemText);
- break;
- }
- }
- }
- /*** 移动的时候*/
- private void actionMove(int move) {
- for (ItemObject item : itemList) {
- item.move(move);
- }
- invalidate();
- }
- /*** 移动,线程中调用*/
- private void actionThreadMove(int move) {
- for (ItemObject item : itemList) {
- item.move(move);
- }
- Message rMessage = new Message();
- rMessage.what = REFRESH_VIEW;
- handler.sendMessage(rMessage);
- }
- /*** 松开的时候*/
- private void actionUp(int move) {
- int newMove = 0;
- if (move > 0) {
- for (int i = 0; i < itemList.size(); i++) {
- if (itemList.get(i).isSelected()) {
- newMove = (int) itemList.get(i).moveToSelected();
- if (onSelectListener != null)
- onSelectListener.endSelect(itemList.get(i).id,
- itemList.get(i).itemText);
- break;
- }
- }
- } else {
- for (int i = itemList.size() - 1; i >= 0; i--) {
- if (itemList.get(i).isSelected()) {
- newMove = (int) itemList.get(i).moveToSelected();
- if (onSelectListener != null)
- onSelectListener.endSelect(itemList.get(i).id,
- itemList.get(i).itemText);
- break;
- }
- }
- }
- for (ItemObject item : itemList) {
- item.newY(move + 0);
- }
- slowMove(newMove);
- Message rMessage = new Message();
- rMessage.what = REFRESH_VIEW;
- handler.sendMessage(rMessage);
- }
- /*** 缓慢移动*/
- private synchronized void slowMove(final int move) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- // 判断正负
- int m = move > 0 ? move : move * (-1);
- int i = move > 0 ? 1 : (-1);
- // 移动速度
- int speed = 1;
- while (true) {
- m = m - speed;
- if (m <= 0) {
- for (ItemObject item : itemList) {
- item.newY(m * i);
- }
- Message rMessage = new Message();
- rMessage.what = REFRESH_VIEW;
- handler.sendMessage(rMessage);
- try {
- Thread.sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- break;
- }
- for (ItemObject item : itemList) {
- item.newY(speed * i);
- }
- Message rMessage = new Message();
- rMessage.what = REFRESH_VIEW;
- handler.sendMessage(rMessage);
- try {
- Thread.sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- for (ItemObject item : itemList) {
- if (item.isSelected()) {
- if (onSelectListener != null)
- onSelectListener.endSelect(item.id, item.itemText);
- break;
- }
- }
- }
- }).start();
- }
- /**
- * 移动到默认位置
- *
- * @param move
- */
- private void defaultMove(int move) {
- for (ItemObject item : itemList) {
- item.newY(move);
- }
- Message rMessage = new Message();
- rMessage.what = REFRESH_VIEW;
- handler.sendMessage(rMessage);
- }
- /*** 滑动监听*/
- private void onSelectListener() {
- if (onSelectListener == null)
- return;
- for (ItemObject item : itemList) {
- if (item.isSelected()) {
- onSelectListener.selecting(item.id, item.itemText);
- }
- }
- }
- /*** 设置数据 (第一次)*/
- public void setData(ArrayList<String> data) {
- this.dataList = data;
- initData();
- }
- /*** 获取返回项 id*/
- public int getSelected() {
- for (ItemObject item : itemList) {
- if (item.isSelected())
- return item.id;
- }
- return -1;
- }
- /*** 获取返回的内容*/
- public String getSelectedText() {
- for (ItemObject item : itemList) {
- if (item.isSelected())
- return item.itemText;
- }
- return "";
- }
- /*** 是否可用*/
- public boolean isEnable() {
- return isEnable;
- }
- /*** 设置是否可用*/
- public void setEnable(boolean isEnable) {
- this.isEnable = isEnable;
- }
- /*** 设置默认选项*/
- public void setDefault(int index) {
- if (index > itemList.size() - 1)
- return;
- float move = itemList.get(index).moveToSelected();
- defaultMove((int) move);
- }
- /*** 获取列表大小*/
- public int getListSize() {
- if (itemList == null)
- return 0;
- return itemList.size();
- }
- /** 监听*/
- public void setOnSelectListener(OnSelectListener onSelectListener) {
- this.onSelectListener = onSelectListener;
- }
- @SuppressLint("HandlerLeak")
- Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what) {
- case REFRESH_VIEW:
- invalidate();
- break;
- default:
- break;
- }
- }
- };
- /*** 单条内容*/
- private class ItemObject {
- /**id*/
- public int id = 0;
- /*** 内容*/
- public String itemText = "";
- /*** x坐标*/
- public int x = 0;
- /*** y坐标*/
- public int y = 0;
- /*** 移动距离*/
- public int move = 0;
- /*** 字体画笔*/
- private TextPaint textPaint;
- /*** 字体范围矩形*/
- private Rect textRect;
- public ItemObject() {
- super();
- }
- /**
- * 绘制自身
- *
- * @param canvas 画板
- * @param containerWidth 容器宽度
- */
- public void drawSelf(Canvas canvas, int containerWidth) {
- if (textPaint == null) {
- textPaint = new TextPaint();
- textPaint.setAntiAlias(true);
- }
- if (textRect == null)
- textRect = new Rect();
- // 判断是否被选择
- if (isSelected()) {
- textPaint.setColor(selectedColor);
- // 获取距离标准位置的距离
- float moveToSelect = moveToSelected();
- moveToSelect = moveToSelect > 0 ? moveToSelect : moveToSelect * (-1);
- // 计算当前字体大小
- float textSize = normalFont
- + ((selectedFont - normalFont) * (1.0f - moveToSelect / (float) unitHeight));
- textPaint.setTextSize(textSize);
- } else {
- textPaint.setColor(normalColor);
- textPaint.setTextSize(normalFont);
- }
- // 返回包围整个字符串的最小的一个Rect区域
- itemText = (String) TextUtils.ellipsize(itemText, textPaint, containerWidth, TextUtils.TruncateAt.END);
- textPaint.getTextBounds(itemText, 0, itemText.length(), textRect);
- // 判断是否可视
- if (!isInView())
- return;
- // 绘制内容
- canvas.drawText(itemText, x + controlWidth / 2 - textRect.width() / 2,
- y + move + unitHeight / 2 + textRect.height() / 2, textPaint);
- }
- /*** 是否在可视界面内*/
- public boolean isInView() {
- if (y + move > controlHeight || (y + move + unitHeight / 2 + textRect.height() / 2) < 0)
- return false;
- return true;
- }
- /*** 移动距离*/
- public void move(int _move) {
- this.move = _move;
- }
- /*** 设置新的坐标*/
- public void newY(int _move) {
- this.move = 0;
- this.y = y + _move;
- }
- /*** 判断是否在选择区域内*/
- public boolean isSelected() {
- if ((y + move) >= controlHeight / 2 - unitHeight / 2 + lineHeight
- && (y + move) <= controlHeight / 2 + unitHeight / 2 - lineHeight) {
- return true;
- }
- if ((y + move + unitHeight) >= controlHeight / 2 - unitHeight / 2 + lineHeight
- && (y + move + unitHeight) <= controlHeight / 2 + unitHeight / 2 - lineHeight) {
- return true;
- }
- if ((y + move) <= controlHeight / 2 - unitHeight / 2 + lineHeight
- && (y + move + unitHeight) >= controlHeight / 2 + unitHeight / 2 - lineHeight) {
- return true;
- }
- return false;
- }
- /*** 获取移动到标准位置需要的距离*/
- public float moveToSelected() {
- return (controlHeight / 2 - unitHeight / 2) - (y + move);
- }
- }
- /*** 选择监听*/
- public interface OnSelectListener {
- /*** 结束选择*/
- void endSelect(int id, String text);
- /*** 选中的内容*/
- void selecting(int id, String text);
- }
- }