├── ActionBarToast.java
├── AmountEditText.java
├── BaseRecyclerAdapter.java
├── CustomTouchView.java
├── LoadingView.java
├── LogUtil.java
├── MaskView.java
├── PhoneEditText.java
├── README.md
├── ScratchCard.java
├── ScreenSizeUtils.java
├── SharePreUtils.java
├── TimeClock.java
├── TimeUtils.java
├── WaveView.java
├── WordsNavigation.java
└── effectImage
├── 20161105231805323.gif
├── 20161118122207199.png
└── custom_touch_view.gif
/ActionBarToast.java:
--------------------------------------------------------------------------------
1 | package com.azhon.lightmode.view;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ObjectAnimator;
6 | import android.content.Context;
7 | import android.content.res.TypedArray;
8 | import android.support.annotation.IntDef;
9 | import android.support.constraint.ConstraintLayout;
10 | import android.support.v7.app.AppCompatActivity;
11 | import android.view.LayoutInflater;
12 | import android.view.View;
13 | import android.view.ViewGroup;
14 |
15 | import com.azhon.lightmode.R;
16 |
17 | import java.lang.annotation.Retention;
18 | import java.lang.annotation.RetentionPolicy;
19 |
20 | /**
21 | * 文件名: ActionBarToast
22 | * 创建时间: 2018/1/4 on 21:09
23 | * 描述: TODO 仿QQ提示信息弹框
24 | *
25 | * 使用方法(在Activity中):
26 | *
27 | * ActionBarToast barToast = new ActionBarToast(this);
28 | * barToast.showToast(ActionBarToast.LENGTH_SHORT);
29 | *
30 | *
31 | * @author 阿钟
32 | */
33 |
34 | public class ActionBarToast {
35 |
36 | /**
37 | * activity 根视图
38 | */
39 | private ViewGroup decorView;
40 | /**
41 | * activity 根视图
42 | */
43 | private View view;
44 | /**
45 | * 状态栏加上actionBar的高度
46 | */
47 | private int totalHeight;
48 | /**
49 | * 是否正在显示
50 | */
51 | private boolean showing;
52 | /**
53 | * 显示/退出 动画的执行时间
54 | */
55 | private int animationDuration = 150;
56 | /**
57 | * 短时间显示 ms
58 | */
59 | public static final int LENGTH_SHORT = 1500;
60 | /**
61 | * 长时间显示 ms
62 | */
63 | public static final int LENGTH_LONG = 2000;
64 | /**
65 | * 不消失,需要手动调用{@link #cancel()}
66 | */
67 | public static final int LENGTH_SHOW = -1;
68 |
69 | /**
70 | * @ IntDef 代替枚举
71 | */
72 | @IntDef({LENGTH_SHORT, LENGTH_LONG, LENGTH_SHOW})
73 | @Retention(RetentionPolicy.SOURCE)
74 | public @interface Duration {
75 | }
76 |
77 | public ActionBarToast(AppCompatActivity activity) {
78 | decorView = (ViewGroup) activity.getWindow().getDecorView();
79 | view = LayoutInflater.from(activity).inflate(R.layout.item_toast, null);
80 | //设置View的高度
81 | totalHeight = getHeight(activity, view.findViewById(R.id.status_bar));
82 | view.setLayoutParams(new ViewGroup.LayoutParams(
83 | ViewGroup.LayoutParams.MATCH_PARENT, totalHeight));
84 | }
85 |
86 | /**
87 | * 显示
88 | *
89 | * @param duration 显示时长
90 | */
91 | public void showToast(@Duration int duration) {
92 | if (!showing) {
93 | showing = true;
94 | decorView.addView(view);
95 | ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationY", -totalHeight, 0f);
96 | animator.setDuration(animationDuration);
97 | animator.start();
98 | if (duration != LENGTH_SHOW) {
99 | //一段时间后隐藏
100 | view.postDelayed(runnable, duration);
101 | }
102 | }
103 | }
104 |
105 | /**
106 | * 延时执行取消操作
107 | */
108 | private Runnable runnable = new Runnable() {
109 | @Override
110 | public void run() {
111 | cancel();
112 | }
113 | };
114 |
115 | /**
116 | * 取消展示
117 | */
118 | public void cancel() {
119 | if (!showing) {
120 | return;
121 | }
122 | //手动移除异步计时器
123 | view.removeCallbacks(runnable);
124 |
125 | ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationY", 0f, -totalHeight);
126 | animator.setDuration(animationDuration);
127 | animator.addListener(new AnimatorListenerAdapter() {
128 | @Override
129 | public void onAnimationEnd(Animator animation) {
130 | if (view.getParent() != null) {
131 | ((ViewGroup) view.getParent()).removeView(view);
132 | showing = false;
133 | }
134 | }
135 | });
136 | animator.start();
137 | }
138 |
139 | /**
140 | * 是否正在展示
141 | *
142 | * @return {@link #showing}
143 | */
144 | public boolean isShowing() {
145 | return showing;
146 | }
147 |
148 | /**
149 | * 获取状态栏 + 标题栏 的高度
150 | */
151 | private int getHeight(Context context, View v) {
152 | //标题栏
153 | TypedArray values = context.getTheme().obtainStyledAttributes(new int[]{android.R.attr.actionBarSize});
154 | int actionBarHeight = values.getDimensionPixelSize(0, 0);
155 | values.recycle();
156 | //状态栏
157 | int statusBarHeight = 0;
158 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
159 | if (resourceId > 0) {
160 | //设置布局 占位视图的高度
161 | statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
162 |
163 | v.setLayoutParams(new ConstraintLayout.LayoutParams(
164 | ConstraintLayout.LayoutParams.MATCH_PARENT, statusBarHeight));
165 | }
166 | return actionBarHeight + statusBarHeight;
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/AmountEditText.java:
--------------------------------------------------------------------------------
1 | package cn.hjtech.pigeon.common.view;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.AppCompatEditText;
5 | import android.text.Editable;
6 | import android.text.InputFilter;
7 | import android.text.Spanned;
8 | import android.text.TextUtils;
9 | import android.text.TextWatcher;
10 | import android.text.method.DigitsKeyListener;
11 | import android.util.AttributeSet;
12 |
13 | /**
14 | * 2017年5月29日23:59:32
15 | *
16 | * @author 阿钟
17 | * @version 1.0
18 | * TODO 金额输入框,只能输入两位小数
19 | *
20 | * @version 1.1
21 | * TODO 添加文本改变监听
22 | */
23 | public class AmountEditText extends AppCompatEditText implements TextWatcher {
24 |
25 |
26 | public AmountEditText(Context context, AttributeSet attrs) {
27 | super(context, attrs);
28 | init();
29 | }
30 |
31 | public AmountEditText(Context context) {
32 | super(context);
33 | init();
34 | }
35 |
36 | private void init() {
37 | //设置输入框允许输入的类型(正则)
38 | setKeyListener(DigitsKeyListener.getInstance("0123456789."));
39 | //设置输入字符
40 | setFilters(new InputFilter[]{inputFilter});
41 | addTextChangedListener(this);
42 | }
43 |
44 | private InputFilter inputFilter = new InputFilter() {
45 | @Override
46 | public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
47 | // 删除等特殊字符,直接返回
48 | if (TextUtils.isEmpty(source)) {
49 | return null;
50 | }
51 | String dValue = dest.toString();
52 | String[] splitArray = dValue.split("\\.");
53 | if (splitArray.length > 1) {
54 | String dotValue = splitArray[1];
55 | int diff = dotValue.length() + 1 - 2;//2表示输入框的小数位数
56 | if (diff > 0) {
57 | return source.subSequence(start, end - diff);
58 | }
59 | }
60 | return null;
61 | }
62 | };
63 |
64 | @Override
65 | public void onTextChanged(CharSequence s, int start, int before, int count) {
66 | if (TextUtils.isEmpty(s)) {
67 | return;
68 | }
69 | //第一个字符不为小数点
70 | if (s.length() == 1 && s.toString().equals(".")) {
71 | setText("");
72 | return;
73 | }
74 | int counter = counter(s.toString(), '.');
75 | if (counter > 1) {
76 | //小数点第一次出现的位置
77 | int index = s.toString().indexOf('.');
78 | setText(s.subSequence(0, index + 1));
79 | }
80 | setSelection(getText().toString().length());
81 | }
82 |
83 | /**
84 | * 统计一个字符在字符串中出现的次数
85 | *
86 | * @param s 字符串
87 | * @param c 字符
88 | * @return 數量
89 | */
90 | public int counter(String s, char c) {
91 | int count = 0;
92 | for (int i = 0; i < s.length(); i++) {
93 | if (s.charAt(i) == c) {
94 | count++;
95 | }
96 | }
97 | return count;
98 | }
99 |
100 | @Override
101 | public void afterTextChanged(Editable s) {
102 | if (listener != null) {
103 | listener.onTextChanged(getText().toString());
104 | }
105 | }
106 |
107 | @Override
108 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
109 | }
110 |
111 | public OnTextChangeListener listener;
112 |
113 | public void setListener(OnTextChangeListener listener) {
114 | this.listener = listener;
115 | }
116 |
117 | public interface OnTextChangeListener {
118 |
119 | void onTextChanged(String s);
120 |
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/BaseRecyclerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.azhong.adapter;
2 |
3 | import android.support.annotation.DrawableRes;
4 | import android.support.annotation.LayoutRes;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.support.v7.widget.RecyclerView.ViewHolder;
7 | import android.util.SparseArray;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.ImageView;
12 | import android.widget.TextView;
13 |
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | /*
18 | * 文件名: BaseRecyclerAdapter
19 | * 创建者: ZSY
20 | * 创建时间: 2017/3/20 on 9:42
21 | * 描述: TODO RecyclerView的封装适配器
22 | */
23 | public abstract class BaseRecyclerAdapter extends RecyclerView.Adapter
24 | implements View.OnClickListener, View.OnLongClickListener {
25 | /**
26 | * 数据源
27 | */
28 | private List data;
29 | /**
30 | * 布局资源id
31 | */
32 | private int layoutResId;
33 | /**
34 | * 头部布局id
35 | */
36 | private int headView = -1;
37 | /**
38 | * 底部部布局id
39 | */
40 | private int footView = -1;
41 | /**
42 | * 数据显示itemType
43 | */
44 | private final int ITEM_TYPE = 870371;
45 | /**
46 | * 头部itemType
47 | */
48 | private final int HEAD_TYPE = 870372;
49 | /**
50 | * 底部itemType
51 | */
52 | private final int FOOT_TYPE = 870373;
53 | /**
54 | * Item点击事件
55 | */
56 | private onItemClickListener clickListener;
57 | /**
58 | * Item长按事件
59 | */
60 | private onItemLongClickListener longListener;
61 |
62 | public BaseRecyclerAdapter(@LayoutRes int layoutResId, List data) {
63 | this.data = data == null ? new ArrayList() : data;
64 | if (layoutResId != 0) {
65 | this.layoutResId = layoutResId;
66 | } else {
67 | throw new NullPointerException("请设置Item资源id");
68 | }
69 | }
70 |
71 | @Override
72 | public VH onCreateViewHolder(ViewGroup parent, int viewType) {
73 | View view = null;
74 | switch (viewType) {
75 | case ITEM_TYPE:
76 | view = LayoutInflater.from(parent.getContext()).inflate(layoutResId, parent, false);
77 | break;
78 | case HEAD_TYPE:
79 | view = LayoutInflater.from(parent.getContext()).inflate(headView, parent, false);
80 | break;
81 | case FOOT_TYPE:
82 | view = LayoutInflater.from(parent.getContext()).inflate(footView, parent, false);
83 | break;
84 | }
85 | return (VH) new BaseViewHolder(view);
86 | }
87 |
88 | @Override
89 | public int getItemViewType(int position) {
90 | int type = ITEM_TYPE;
91 | //如果添加了头部view
92 | if (position == 0 && headView != -1) {
93 | type = HEAD_TYPE;
94 | }
95 | //如果添加了底部view
96 | if (position == getItemCount() - 1 && footView != -1) {
97 | type = FOOT_TYPE;
98 | }
99 | return type;
100 | }
101 |
102 | @Override
103 | public void onBindViewHolder(VH holder, int position) {
104 | switch (holder.getItemViewType()) {
105 | case ITEM_TYPE:
106 | //设置Item的点击事件
107 | holder.itemView.setOnClickListener(this);
108 | //设置Item的长按事件
109 | holder.itemView.setOnLongClickListener(this);
110 | if (headView == -1) {
111 | holder.itemView.setTag(position);
112 | bindTheData(holder, data.get(position), position);
113 | } else {
114 | holder.itemView.setTag(position - 1);
115 | bindTheData(holder, data.get(position - 1), position - 1);
116 | }
117 | break;
118 | case HEAD_TYPE:
119 | break;
120 | case FOOT_TYPE:
121 | break;
122 | }
123 | }
124 |
125 | @Override
126 | public int getItemCount() {
127 | if (headView != -1 && footView != -1) {
128 | return data.size() + 2;
129 | }
130 | if (headView != -1) {
131 | return data.size() + 1;
132 | }
133 | if (footView != -1) {
134 | return data.size() + 1;
135 | }
136 | return data.size();
137 | }
138 |
139 | /**
140 | * 清除数据
141 | */
142 | public void clearData() {
143 | if (data != null && data.size() > 0) {
144 | data.clear();
145 | }
146 | notifyDataSetChanged();
147 | }
148 |
149 | /**
150 | * 绑定数据
151 | *
152 | * @param holder 视图管理者
153 | * @param data 数据源
154 | */
155 | protected abstract void bindTheData(VH holder, D data, int position);
156 |
157 | /**
158 | * 添加头部View
159 | */
160 | public void addHeadView(@LayoutRes int id) {
161 | this.headView = id;
162 | }
163 |
164 | /**
165 | * 添加底部View
166 | */
167 | public void addFootView(@LayoutRes int id) {
168 | this.footView = id;
169 | }
170 |
171 | @Override
172 | public void onClick(View v) {
173 | //点击回调
174 | if (clickListener != null) {
175 | clickListener.onItemClick((Integer) v.getTag(), v);
176 | }
177 | }
178 |
179 | @Override
180 | public boolean onLongClick(View v) {
181 | //长按回调
182 | return longListener != null && longListener.onItemLonClick((Integer) v.getTag(), v);
183 | }
184 |
185 | /*********************************************
186 | * 基础视图管理者
187 | *********************************************/
188 | public class BaseViewHolder extends ViewHolder {
189 | /**
190 | * 集合类,layout里包含的View,以view的id作为key,value是view对象
191 | */
192 | private SparseArray mViews;
193 |
194 | public BaseViewHolder(View itemView) {
195 | super(itemView);
196 | mViews = new SparseArray<>();
197 | }
198 |
199 | public T findViewById(int viewId) {
200 | View view = mViews.get(viewId);
201 | if (view == null) {
202 | view = itemView.findViewById(viewId);
203 | mViews.put(viewId, view);
204 | }
205 | return (T) view;
206 | }
207 |
208 | /**
209 | * 设置文本资源
210 | *
211 | * @param viewId view id
212 | * @param s 字符
213 | */
214 | public TextView setText(int viewId, CharSequence s) {
215 | TextView view = findViewById(viewId);
216 | view.setText(s);
217 | return view;
218 | }
219 |
220 | /**
221 | * 设置图片资源
222 | *
223 | * @param viewId view id
224 | * @param imageResId 图片资源id
225 | */
226 | public ImageView setImageResource(int viewId, @DrawableRes int imageResId) {
227 | ImageView view = findViewById(viewId);
228 | view.setImageResource(imageResId);
229 | return view;
230 | }
231 |
232 |
233 | }
234 |
235 | /**
236 | * 设置点击监听
237 | *
238 | * @param clickListener 监听器
239 | */
240 | public void setItemClickListener(onItemClickListener clickListener) {
241 | this.clickListener = clickListener;
242 | }
243 |
244 | /**
245 | * 设置长按监听
246 | *
247 | * @param longListener 监听器
248 | */
249 | public void setItemLongClickListener(onItemLongClickListener longListener) {
250 | this.longListener = longListener;
251 | }
252 |
253 | public interface onItemClickListener {
254 | void onItemClick(int position, View v);
255 | }
256 |
257 | public interface onItemLongClickListener {
258 | boolean onItemLonClick(int position, View v);
259 | }
260 | }
--------------------------------------------------------------------------------
/CustomTouchView.java:
--------------------------------------------------------------------------------
1 | package com.azhon.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Rect;
8 | import android.util.AttributeSet;
9 | import android.view.MotionEvent;
10 | import android.view.VelocityTracker;
11 | import android.view.View;
12 | import android.view.ViewConfiguration;
13 | import android.widget.OverScroller;
14 |
15 | import androidx.annotation.Nullable;
16 |
17 | import java.util.HashMap;
18 | import java.util.Map;
19 | import java.util.Random;
20 |
21 | /**
22 | * 文件名: CustomTouchView
23 | * 创建时间: 2019-04-26 on 15:51
24 | * 描述: TODO 自定义View,触摸交互
25 | *
26 | * @author 阿钟
27 | */
28 |
29 | public class CustomTouchView extends View {
30 | /**
31 | * 上下文
32 | */
33 | private Context context;
34 | /**
35 | * 每一个矩形的宽度
36 | */
37 | private int rectWidth = 120;
38 | /**
39 | * 绘制多少个矩形
40 | */
41 | private int rectCount = 23;
42 | /**
43 | * 宽度
44 | */
45 | private int width;
46 | /**
47 | * 高度
48 | */
49 | private int height;
50 | /**
51 | * 内容区域的宽度
52 | */
53 | private int contentWidth;
54 | /**
55 | * 矩形画笔
56 | */
57 | private Paint paint;
58 | /**
59 | * 文字画笔
60 | */
61 | private Paint textPaint;
62 | /**
63 | * 速度获取
64 | */
65 | private VelocityTracker velocityTracker;
66 | /**
67 | * 惯性最大 最小速度
68 | */
69 | private int maximumVelocity, minimumVelocity;
70 | /**
71 | * 控制滑动
72 | */
73 | private OverScroller scroller;
74 | /**
75 | * 非法触控id
76 | */
77 | private final static int INVALID_ID = -1;
78 | /**
79 | * 记录首个触控点的id 避免多点触控引起的滚动
80 | */
81 | private int activePointerId = INVALID_ID;
82 | /**
83 | * 最小可滑动值、最大可滑动值
84 | */
85 | private int minScrollX = 0, maxScrollX = 0;
86 | /**
87 | * 手指上次滑动的点
88 | */
89 | private float lastX;
90 |
91 | /**
92 | * 保存随机生成的颜色
93 | */
94 | private Map colors = new HashMap<>();
95 |
96 |
97 | public CustomTouchView(Context context) {
98 | this(context, null);
99 | }
100 |
101 | public CustomTouchView(Context context, @Nullable AttributeSet attrs) {
102 | super(context, attrs);
103 | this.context = context;
104 |
105 | paint = new Paint();
106 | paint.setColor(Color.MAGENTA);
107 | paint.setAntiAlias(true);
108 |
109 | textPaint = new Paint();
110 | textPaint.setColor(Color.WHITE);
111 | textPaint.setTextSize(32);
112 | textPaint.setStyle(Paint.Style.FILL);
113 | //该方法即为设置基线上那个点究竟是left,center,还是right 这里我设置为center
114 | textPaint.setTextAlign(Paint.Align.CENTER);
115 |
116 | //最小的惯性滚动速度
117 | maximumVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();
118 | minimumVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
119 |
120 | scroller = new OverScroller(context);
121 |
122 | }
123 |
124 | @Override
125 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
126 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
127 | int defaultWidth = DensityUtil.dip2px(context, 300);
128 | int defaultHeight = DensityUtil.dip2px(context, 200);
129 | width = getViewSize(defaultWidth, widthMeasureSpec);
130 | height = getViewSize(defaultHeight, heightMeasureSpec);
131 | //设置View的大小
132 | setMeasuredDimension(width, height);
133 |
134 | //最大的滚动距离为内容的宽度 - View的宽度 - 最右边多算的一个空白区域
135 | contentWidth = rectCount * rectWidth * 2 - rectWidth;
136 | maxScrollX = contentWidth - width;
137 | }
138 |
139 | @Override
140 | protected void onDraw(Canvas canvas) {
141 | super.onDraw(canvas);
142 | for (int i = 0; i < rectCount; i++) {
143 | int right = i * 2 * rectWidth + rectWidth;
144 | Rect rect = new Rect(i * 2 * rectWidth, height / 4, right, 3 * height / 4);
145 | paint.setColor(randomColor(i));
146 | canvas.drawRect(rect, paint);
147 | drawText(canvas, rect, String.valueOf(i + 1), textPaint);
148 | }
149 | }
150 |
151 | /**
152 | * 将文字绘制在矩形中间
153 | */
154 | private void drawText(Canvas canvas, Rect rect, String text, Paint paint) {
155 | Paint.FontMetrics fontMetrics = paint.getFontMetrics();
156 | //为基线到字体上边框的距离,即上图中的top
157 | float top = fontMetrics.top;
158 | //为基线到字体下边框的距离,即上图中的bottom
159 | float bottom = fontMetrics.bottom;
160 | int baseLineY = (int) (rect.centerY() - top / 2 - bottom / 2);
161 | //基线中间点的y轴计算公式
162 | canvas.drawText(text, rect.centerX(), baseLineY, paint);
163 | }
164 |
165 | /**
166 | * 获取View的宽高
167 | */
168 | private int getViewSize(int defaultSize, int measureSpec) {
169 | int mode = MeasureSpec.getMode(measureSpec);
170 | int size = MeasureSpec.getSize(measureSpec);
171 | switch (mode) {
172 | //wrap_content
173 | case MeasureSpec.AT_MOST:
174 | case MeasureSpec.UNSPECIFIED:
175 | return defaultSize;
176 | //match_parent、固定dp大小
177 | case MeasureSpec.EXACTLY:
178 | return size;
179 | }
180 | return defaultSize;
181 | }
182 |
183 |
184 | @Override
185 | public boolean onTouchEvent(MotionEvent event) {
186 | //内容区域的宽度是否大于View的宽度,如果没有大于View的宽度则不需要滚动
187 | if (contentWidth <= width) return super.onTouchEvent(event);
188 | //开始速度检测
189 | if (velocityTracker == null) {
190 | velocityTracker = VelocityTracker.obtain();
191 | }
192 | velocityTracker.addMovement(event);
193 | switch (event.getAction()) {
194 | case MotionEvent.ACTION_DOWN:
195 | //记录首个触控点的id
196 | activePointerId = event.findPointerIndex(event.getActionIndex());
197 | if (!scroller.isFinished()) {
198 | scroller.abortAnimation();
199 | }
200 | lastX = event.getX();
201 | break;
202 | case MotionEvent.ACTION_MOVE:
203 | if (activePointerId == INVALID_ID || event.findPointerIndex(activePointerId) == INVALID_ID) {
204 | break;
205 | }
206 | //计算首个触控点移动后的坐标
207 | float moveX = lastX - event.getX(activePointerId);
208 | lastX = event.getX(activePointerId);
209 | damping((int) moveX);
210 | break;
211 | case MotionEvent.ACTION_UP:
212 | activePointerId = INVALID_ID;
213 | //处理松手后的Fling
214 | velocityTracker.computeCurrentVelocity(1000, maximumVelocity);
215 | int velocityX = (int) velocityTracker.getXVelocity();
216 | //滑动速度大于默认速度,则开始惯性滑动
217 | if (Math.abs(velocityX) > minimumVelocity) {
218 | startFling(velocityX);
219 | }
220 | restoreLeft();
221 | restoreRight();
222 | recycleVelocity();
223 | break;
224 | case MotionEvent.ACTION_CANCEL:
225 | activePointerId = INVALID_ID;
226 | if (!scroller.isFinished()) {
227 | scroller.abortAnimation();
228 | }
229 | recycleVelocity();
230 | break;
231 | }
232 | return true;
233 | }
234 |
235 |
236 | /**
237 | * 当持续向右滑动的时候,放手回到起点
238 | */
239 | private void restoreLeft() {
240 | //不能继续往右边滑动了
241 | if (getScrollX() < 0) {
242 | startFling(-50);
243 | invalidate();
244 | }
245 | }
246 |
247 | /**
248 | * 当持续向左滑动的时候,放手定位到最右边
249 | */
250 | private void restoreRight() {
251 | //不能继续往右边滑动了
252 | if (getScrollX() > maxScrollX) {
253 | startFling(50);
254 | invalidate();
255 | }
256 | }
257 |
258 | /**
259 | * 阻尼效果
260 | */
261 | private void damping(int moveX) {
262 | //持续在向左滑动 和 持续向右滑动
263 | if (getScrollX() > maxScrollX || getScrollX() < minScrollX) {
264 | scrollBy(moveX / 3, 0);
265 | } else {
266 | scrollBy(moveX, 0);
267 | }
268 | }
269 |
270 | /**
271 | * VelocityTracker回收
272 | */
273 | private void recycleVelocity() {
274 | if (velocityTracker != null) {
275 | velocityTracker.recycle();
276 | velocityTracker = null;
277 | }
278 | }
279 |
280 |
281 | /**
282 | * 开始的、惯性滚动
283 | *
284 | * @param velocityX x轴速度
285 | */
286 | private void startFling(int velocityX) {
287 | scroller.fling(getScrollX(), 0, -velocityX / 2, 0, minScrollX, maxScrollX, 0, 0);
288 | invalidate();
289 | }
290 |
291 |
292 | /**
293 | * 由fling方法发起的invalidate,会最终调用view的computeScroll方法
294 | * 而在computeScroll方法中又发起了postInvalidate,最终又会调用view的computeScroll,如此循环绘制形成了惯性滚动
295 | */
296 | @Override
297 | public void computeScroll() {
298 | if (scroller.computeScrollOffset()) {
299 | scrollTo(scroller.getCurrX(), scroller.getCurrY());
300 | postInvalidate();
301 | }
302 | }
303 |
304 | /**
305 | * 随机生成颜色
306 | */
307 | private int randomColor(int position) {
308 | Integer color = colors.get(position);
309 | if (color != null) {
310 | return color;
311 | }
312 | Random random = new Random();
313 | int r = random.nextInt(256);
314 | int g = random.nextInt(256);
315 | int b = random.nextInt(256);
316 | color = Color.rgb(r, g, b);
317 | colors.put(position, color);
318 | return color;
319 | }
320 | }
321 |
--------------------------------------------------------------------------------
/LoadingView.java:
--------------------------------------------------------------------------------
1 | package com.zsy.roate;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.Paint;
9 | import android.util.AttributeSet;
10 | import android.view.View;
11 | import android.view.animation.BounceInterpolator;
12 |
13 | /*
14 | *以下为自定义的属性,如需使用需在attrs.xml文件中添加以下代码
15 | *
16 | *
17 | *
18 | *
19 | *
20 | *
21 | *
22 | *
23 | *
24 | *
25 | *
26 | *
27 | *
28 | *
29 | *
30 | *
31 | *
32 | *
33 | */
34 | /*
35 | * 文件名: LoadingView
36 | * 创建者: 阿钟
37 | * 创建时间: 2016/11/9 22:05
38 | * 描述: 自定义菊花进度条
39 | */
40 | public class LoadingView extends View {
41 |
42 | /*view的默认宽度*/
43 | private int mWidth;
44 | /*view的默认高度*/
45 | private int mHeight;
46 | /*线条粗细*/
47 | private int paintBold;
48 | /*线条长度*/
49 | private int lineLength;
50 | /*背景线条颜色*/
51 | private int bgPaintColor;
52 | /*上层线条颜色*/
53 | private int beforePaintColor;
54 | /*进度文字颜色*/
55 | private int textColor;
56 | /*线条个数*/
57 | private int lines;
58 | /*背景画笔*/
59 | private Paint bgPaint;
60 | /*前景画笔*/
61 | private Paint bfPaint;
62 | /*进度文字画笔*/
63 | private Paint textPaint;
64 | /*当前下载进度*/
65 | private int progress;
66 | /*最大进度*/
67 | private int max;
68 |
69 | public LoadingView(Context context) {
70 | super(context);
71 | }
72 |
73 | public LoadingView(Context context, AttributeSet attrs) {
74 | super(context, attrs);
75 | loadAttrs(context, attrs);
76 | initPaint();
77 | }
78 |
79 | public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
80 | super(context, attrs, defStyleAttr);
81 | }
82 |
83 | /**
84 | * 加载我们在attrs.xml文件的自定义的属性
85 | */
86 | private void loadAttrs(Context context, AttributeSet attrs) {
87 | TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.a_zhon);
88 | paintBold = array.getDimensionPixelSize(R.styleable.a_zhon_paintBold, 10);
89 | lineLength = array.getDimensionPixelSize(R.styleable.a_zhon_lineLength, 25);
90 | bgPaintColor = array.getColor(R.styleable.a_zhon_backgroundColor, Color.GRAY);
91 | beforePaintColor = array.getColor(R.styleable.a_zhon_beforeColor, Color.YELLOW);
92 | lines = array.getInt(R.styleable.a_zhon_lines, 20);
93 | max = array.getInt(R.styleable.a_zhon_max, 100);
94 | progress = array.getInt(R.styleable.a_zhon_progress, 0);
95 | textColor = array.getColor(R.styleable.a_zhon_textColor, Color.BLACK);
96 | array.recycle();
97 | }
98 |
99 | /**
100 | * 初始化画笔
101 | */
102 | private void initPaint() {
103 | bgPaint = new Paint();
104 | bgPaint.setColor(bgPaintColor);
105 | bgPaint.setAntiAlias(true);
106 | bgPaint.setStrokeWidth(paintBold);
107 | //使得画笔更加圆滑
108 | bgPaint.setStrokeJoin(Paint.Join.ROUND);
109 | bgPaint.setStrokeCap(Paint.Cap.ROUND);
110 |
111 | bfPaint = new Paint();
112 | bfPaint.setColor(beforePaintColor);
113 | bfPaint.setAntiAlias(true);
114 | bfPaint.setStrokeWidth(paintBold);
115 | bfPaint.setStrokeJoin(Paint.Join.ROUND);
116 | bfPaint.setStrokeCap(Paint.Cap.ROUND);
117 |
118 | textPaint = new Paint();
119 | textPaint.setColor(textColor);
120 | textPaint.setAntiAlias(true);
121 | textPaint.setTextAlign(Paint.Align.CENTER);
122 | textPaint.setTextSize(40);
123 | }
124 |
125 | @Override
126 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
127 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
128 | //获取view的宽度
129 | mWidth = getViewSize(100, widthMeasureSpec);
130 | //获取view的高度
131 | mHeight = getViewSize(100, heightMeasureSpec);
132 | }
133 |
134 | /**
135 | * 测量模式 表示意思
136 | * UNSPECIFIED 父容器没有对当前View有任何限制,当前View可以任意取尺寸
137 | * EXACTLY 当前的尺寸就是当前View应该取的尺寸
138 | * AT_MOST 当前尺寸是当前View能取的最大尺寸
139 | *
140 | * @param defaultSize 默认大小
141 | * @param measureSpec 包含测量模式和宽高信息
142 | * @return 返回View的宽高大小
143 | */
144 | private int getViewSize(int defaultSize, int measureSpec) {
145 | int viewSize = defaultSize;
146 | //获取测量模式
147 | int mode = MeasureSpec.getMode(measureSpec);
148 | //获取大小
149 | int size = MeasureSpec.getSize(measureSpec);
150 | switch (mode) {
151 | case MeasureSpec.UNSPECIFIED: //如果没有指定大小,就设置为默认大小
152 | viewSize = defaultSize;
153 | break;
154 | case MeasureSpec.AT_MOST: //如果测量模式是最大取值为size
155 | //我们将大小取最大值,你也可以取其他值
156 | viewSize = size;
157 | break;
158 | case MeasureSpec.EXACTLY: //如果是固定的大小,那就不要去改变它
159 | viewSize = size;
160 | break;
161 | }
162 | return viewSize;
163 | }
164 |
165 | @Override
166 | protected void onDraw(Canvas canvas) {
167 | super.onDraw(canvas);
168 | int x = mWidth / 2;
169 | int y = mHeight / 2;
170 | int r = x - 5;
171 | for (int i = 0; i < lines; i++) {
172 | //绘制下层菊花
173 | canvas.drawLine(x, y - r, x, y - r + lineLength, bgPaint);
174 | canvas.rotate(360 / lines, x, y);
175 | }
176 | //获取需要绘制多少个刻度
177 | int count = (progress * lines) / max;
178 | //绘制中间的文字进度
179 | canvas.drawText((progress * 100 / max) + "%", x, y + 5, textPaint);
180 | //绘制上层菊花,也就是进度
181 | canvas.rotate(360 / lines, x, y);
182 | for (; count > 0; count--) {
183 | canvas.drawLine(x, y - r, x, y - r + lineLength, bfPaint);
184 | canvas.rotate(360 / lines, x, y);
185 | }
186 | }
187 |
188 | /**
189 | * 为进度设置动画
190 | * ValueAnimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的,
191 | * 而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。
192 | * 它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,
193 | * 我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,
194 | * 那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。
195 | *
196 | * @param start 开始值
197 | * @param current 结束值
198 | * @param duration 动画时长
199 | */
200 | public void startAnimation(int start, int current, int duration) {
201 | ValueAnimator progressAnimator = ValueAnimator.ofInt(start, current);
202 | progressAnimator.setDuration(duration);
203 | progressAnimator.setTarget(progress);
204 | progressAnimator.setInterpolator(new BounceInterpolator());
205 | progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
206 | @Override
207 | public void onAnimationUpdate(ValueAnimator animation) {
208 | progress = (int) animation.getAnimatedValue();
209 | invalidate();
210 | }
211 | });
212 | progressAnimator.start();
213 | }
214 |
215 | /*设置进度最大值*/
216 | public void setMax(int max) {
217 | this.max = max;
218 | invalidate();
219 | }
220 |
221 | /*设置当前进度*/
222 | public void setProgress(int progress) {
223 | this.progress = progress;
224 | invalidate();
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/LogUtil.java:
--------------------------------------------------------------------------------
1 | package com.zsy.lambda;
2 |
3 | /*
4 | * 文件名: LogUtil
5 | * 创建者: 阿钟
6 | * 创建时间: 2016/10/22 20:21
7 | * 描述: 输出Log的工具类
8 | */
9 |
10 | import android.util.Log;
11 |
12 | public class LogUtil {
13 |
14 | //Log开关
15 | private static boolean isOpen = true;
16 |
17 | /**
18 | * 设置是否输出Log
19 | *
20 | * @param b
21 | */
22 | public static void setSwitch(boolean b) {
23 | isOpen = b;
24 | }
25 |
26 | //**********************Debug***********//
27 | public static void d(String tag, String msg) {
28 | if (isOpen) Log.d(tag, msg);
29 | }
30 |
31 | public static void d(String tag, int msg) {
32 | if (isOpen) Log.d(tag, String.valueOf(msg));
33 | }
34 |
35 | public static void d(String tag, float msg) {
36 | if (isOpen) Log.d(tag, String.valueOf(msg));
37 | }
38 |
39 | public static void d(String tag, long msg) {
40 | if (isOpen) Log.d(tag, String.valueOf(msg));
41 | }
42 |
43 | public static void d(String tag, double msg) {
44 | if (isOpen) Log.d(tag, String.valueOf(msg));
45 | }
46 |
47 | public static void d(String tag, boolean msg) {
48 | if (isOpen) Log.d(tag, String.valueOf(msg));
49 | }
50 |
51 | //**********************Info***********//
52 | public static void i(String tag, String msg) {
53 | if (isOpen) Log.i(tag, msg);
54 | }
55 |
56 | public static void i(String tag, int msg) {
57 | if (isOpen) Log.i(tag, String.valueOf(msg));
58 | }
59 |
60 | public static void i(String tag, float msg) {
61 | if (isOpen) Log.i(tag, String.valueOf(msg));
62 | }
63 |
64 |
65 | public static void i(String tag, long msg) {
66 | if (isOpen) Log.i(tag, String.valueOf(msg));
67 | }
68 |
69 | public static void i(String tag, double msg) {
70 | if (isOpen) Log.i(tag, String.valueOf(msg));
71 | }
72 |
73 | public static void i(String tag, boolean msg) {
74 | if (isOpen) Log.i(tag, String.valueOf(msg));
75 | }
76 |
77 | //**********************Error***********//
78 | public static void e(String tag, String msg) {
79 | if (isOpen) Log.e(tag, msg);
80 | }
81 |
82 | public static void e(String tag, int msg) {
83 | if (isOpen) Log.e(tag, String.valueOf(msg));
84 | }
85 |
86 | public static void e(String tag, float msg) {
87 | if (isOpen) Log.e(tag, String.valueOf(msg));
88 | }
89 |
90 | public static void e(String tag, Long msg) {
91 | if (isOpen) Log.e(tag, String.valueOf(msg));
92 | }
93 |
94 | public static void e(String tag, double msg) {
95 | if (isOpen) Log.e(tag, String.valueOf(msg));
96 | }
97 |
98 | public static void e(String tag, boolean msg) {
99 | if (isOpen) Log.e(tag, String.valueOf(msg));
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/MaskView.java:
--------------------------------------------------------------------------------
1 | package com.azhon.lightmode.view;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.graphics.Path;
9 | import android.graphics.RectF;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.view.ViewParent;
13 | import android.view.animation.AlphaAnimation;
14 | import android.view.animation.Animation;
15 |
16 | /**
17 | * 项目名: LightMode
18 | * 包名 com.azhon.lightmode
19 | * 文件名: MaskView
20 | * 创建时间: 2018/1/2 on 23:14
21 | * 描述: TODO 模仿网页观看视频,关灯效果;需要在完全显示在屏幕上时,添加才有效
22 | *
23 | * @author 阿钟
24 | */
25 |
26 | public class MaskView extends View {
27 |
28 | /**
29 | * activity 根视图
30 | */
31 | private ViewGroup decorView;
32 | /**
33 | * 不加遮罩的视图
34 | */
35 | private View brightView;
36 | /**
37 | * 遮罩画笔
38 | */
39 | private Paint paint;
40 | /**
41 | * 不遮罩的view
42 | */
43 | private RectF brightRectF;
44 | /**
45 | * 遮罩的路径
46 | */
47 | private Path path;
48 |
49 | /**
50 | * 是否已经添加了
51 | */
52 | private boolean showing = false;
53 |
54 | public MaskView(Context context) {
55 | super(context);
56 | init(context);
57 | }
58 |
59 | private void init(Context context) {
60 | //获取activity顶层视图
61 | decorView = (ViewGroup) ((Activity) context).getWindow().getDecorView();
62 | //初始化蒙版视图,充满全屏幕
63 | setLayoutParams(new ViewGroup.LayoutParams(
64 | ViewGroup.LayoutParams.MATCH_PARENT,
65 | ViewGroup.LayoutParams.MATCH_PARENT));
66 | paint = new Paint();
67 | //设置为黑色 在加点透明度
68 | paint.setColor(Color.argb(230, 0, 0, 0));
69 | path = new Path();
70 |
71 | }
72 |
73 | @Override
74 | protected void onDraw(Canvas canvas) {
75 | super.onDraw(canvas);
76 | if (decorView == null || brightView == null) {
77 | return;
78 | }
79 | //路径恢复
80 | path.reset();
81 | //A
82 | path.moveTo(brightRectF.left, brightRectF.top);
83 | //B
84 | path.lineTo(brightRectF.right, brightRectF.top);
85 | //C
86 | path.lineTo(brightRectF.right, brightRectF.bottom);
87 | //D
88 | path.lineTo(brightRectF.left, brightRectF.bottom);
89 | //A
90 | path.lineTo(brightRectF.left, brightRectF.top);
91 | //E
92 | path.lineTo(0, brightRectF.top);
93 | //F
94 | path.lineTo(0, decorView.getHeight());
95 | //F
96 | path.lineTo(decorView.getWidth(), decorView.getHeight());
97 | //H
98 | path.lineTo(decorView.getWidth(), 0);
99 | //I
100 | path.lineTo(0, 0);
101 | //E
102 | path.lineTo(0, brightRectF.top);
103 | //A
104 | path.lineTo(brightRectF.left, brightRectF.top);
105 | //闭合曲线
106 | path.close();
107 | canvas.drawPath(path, paint);
108 | }
109 |
110 | /**
111 | * 添加不加蒙版的View
112 | *
113 | * @param view 视图
114 | */
115 | public void attachView(View view) {
116 | if (!showing) {
117 | this.brightView = view;
118 | //没有添加,才可以添加
119 | initMask();
120 | }
121 | }
122 |
123 | /**
124 | * 移除遮罩
125 | */
126 | public void removeMask() {
127 | if (!showing) {
128 | //没有添加
129 | return;
130 | }
131 | //使用动画消失
132 | AlphaAnimation animation = new AlphaAnimation(1f, 0f);
133 | animation.setDuration(500);
134 | animation.setAnimationListener(new Animation.AnimationListener() {
135 | @Override
136 | public void onAnimationStart(Animation animation) {
137 | }
138 |
139 | @Override
140 | public void onAnimationEnd(Animation animation) {
141 | ViewParent parent = MaskView.this.getParent();
142 | if (parent != null && parent instanceof ViewGroup) {
143 | ((ViewGroup) parent).removeView(MaskView.this);
144 | showing = false;
145 | }
146 | }
147 |
148 | @Override
149 | public void onAnimationRepeat(Animation animation) {
150 |
151 | }
152 | });
153 | startAnimation(animation);
154 |
155 | }
156 |
157 | /**
158 | * 初始化不加遮罩view的位置
159 | */
160 | private void initMask() {
161 | int[] location = new int[2];
162 | //获取view在屏幕上的坐标
163 | brightView.getLocationOnScreen(location);
164 | brightRectF = new RectF(location[0], location[1], location[0] + brightView.getWidth(),
165 | location[1] + brightView.getHeight());
166 | //添加蒙版到Activity之上
167 | decorView.addView(this);
168 | invalidate();
169 | showing = true;
170 | }
171 |
172 | /**
173 | * 是否已经添加
174 | *
175 | * @return
176 | */
177 | public boolean isShowing() {
178 | return showing;
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/PhoneEditText.java:
--------------------------------------------------------------------------------
1 | package com.aiche.recharge.view;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.AppCompatEditText;
5 | import android.text.Editable;
6 | import android.text.InputFilter;
7 | import android.text.Selection;
8 | import android.text.TextWatcher;
9 | import android.text.method.DigitsKeyListener;
10 | import android.util.AttributeSet;
11 |
12 | /**
13 | * 文件名: PhoneEditText
14 | * 创建时间: 2018/4/17 on 14:51
15 | * 描述: TODO 一个自动分割手机号码的输入框
16 | * eg:132 xxxx xxx
17 | * 当然中间这个分割符号你可以自己定义
18 | * 修改这个属性的值即可{@link flag},(目前支持分隔符只占一个长度)
19 | *
20 | *
21 | * @author 阿钟
22 | */
23 |
24 | public class PhoneEditText extends AppCompatEditText implements TextWatcher {
25 | /**
26 | * 分割符
27 | */
28 | private final String flag = " ";
29 |
30 | public PhoneEditText(Context context) {
31 | super(context);
32 | init();
33 | }
34 |
35 | public PhoneEditText(Context context, AttributeSet attrs) {
36 | super(context, attrs);
37 | init();
38 | }
39 |
40 | private void init() {
41 | //设置输入框允许输入的类型(正则)
42 | setKeyListener(DigitsKeyListener.getInstance("0123456789"));
43 | //设置输入字符
44 | addTextChangedListener(this);
45 | InputFilter.LengthFilter filter = new InputFilter.LengthFilter(13);
46 | setFilters(new InputFilter[]{filter});
47 | }
48 |
49 | @Override
50 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
51 |
52 | }
53 |
54 | @Override
55 | public void onTextChanged(CharSequence s, int start, int before, int count) {
56 | StringBuilder sb = new StringBuilder(s);
57 | int length = sb.length();
58 | if (before == 0) {
59 | //输入内容
60 | if (length == 4) {
61 | sb.insert(3, flag);
62 | setText(sb.toString());
63 | } else if (length == 9) {
64 | sb.insert(8, flag);
65 | setText(sb.toString());
66 | }
67 | } else if (length > 0) {
68 | //删除内容
69 | if (length == 4) {
70 | sb.deleteCharAt(3);
71 | setText(sb.toString());
72 | } else if (length == 9) {
73 | sb.deleteCharAt(8);
74 | setText(sb.toString());
75 | }
76 | }
77 | Selection.setSelection(getAllText(), sb.length());
78 | }
79 |
80 | /**
81 | * 获取实际的手机号
82 | *
83 | * @return 没有分割符号的字符串
84 | */
85 | public String getRealPhone() {
86 | String editable = super.getText().toString();
87 | String number = editable.replaceAll(flag, "");
88 | return number;
89 | }
90 |
91 | @Override
92 | public void setText(CharSequence text, BufferType type) {
93 | if (text.length() == 11) {
94 | StringBuilder sb = new StringBuilder(text);
95 | sb.insert(3, flag).insert(8, flag);
96 | setText(sb.toString());
97 | } else {
98 | super.setText(text, type);
99 | }
100 | }
101 |
102 | public Editable getAllText() {
103 | return super.getText();
104 | }
105 |
106 | @Override
107 | public void afterTextChanged(Editable s) {
108 |
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 个人整理的一个工具类的集合,包括一些自定义的View
2 |
3 | ### ● TimeClock.java——>自定义时钟View——>[使用方法](http://blog.csdn.net/a_zhon/article/details/53027501)——>效果图
4 |
5 |
6 |
7 | ### ● LogUtil.java——>Log日志工具类
8 |
9 | ### ● ScratchCard.java——>刮刮卡效果——>效果图
10 |
11 |
12 |
13 | ### ● LoadingView.java——>菊花进度——>[使用方法](http://blog.csdn.net/a_zhon/article/details/53143034)——>效果图
14 |
15 |
16 |
17 | ### ● WordsNavigation.java——>联系人列表字母索引——>[使用方法](http://blog.csdn.net/a_zhon/article/details/53214849)——>效果图
18 |
19 |
20 |
21 | ### ● BaseRecyclerAdapter.java——>RecyclerView适配器——>[使用方法](http://blog.csdn.net/a_zhon/article/details/66971369)
22 |
23 | ```
24 | public class MyAdapter extends BaseRecyclerAdapter {
25 |
26 | public MyAdapter(int layoutResId, List data) {
27 | super(layoutResId, data);
28 | }
29 |
30 | @Override
31 | protected void bindTheData(BaseRecyclerAdapter.BaseViewHolder holder, DataBean data, int position) {
32 | holder.setText(android.R.id.text1, data.getName());
33 | }
34 | }
35 | ```
36 |
37 | ---
38 |
39 | ```
40 | MyAdapter adapter = new MyAdapter(android.R.layout.simple_list_item_1, list);
41 | //添加头布局
42 | adapter.addHeadView(R.layout.head_view);
43 | //添加尾布局
44 | adapter.addFootView(R.layout.foot_view);
45 | recyclerView.setAdapter(adapter);
46 | adapter.setClickListener(new BaseRecyclerAdapter.onItemClickListener() {
47 | @Override
48 | public void onItemClick(int position, View v) {
49 | Toast.makeText(MainActivity.this, "点击 position = " + position, Toast.LENGTH_SHORT).show();
50 | }
51 | });
52 | ```
53 |
54 | ### ● ScreenSizeUtils.java——>获取手机屏幕大小
55 |
56 | ### ● SharePreUtils.java——>SharedPreferences数据存储工具类
57 |
58 | ### ● AmountEditText.java——>只允许输入两位小数的输入框
59 |
60 | ```
61 | //添加文本改变监听
62 | editeText.setListener(new AmountEditText.OnTextChangeListener() {
63 | @Override
64 | public void onTextChanged(String s) {
65 | //回调
66 | }
67 | });
68 | ```
69 |
70 | ### ● TimeUtils.java——>时间戳转化为年月日格式
71 |
72 | ### ● WaveView.java——>水波球——>[使用方法](http://blog.csdn.net/a_zhon/article/details/77842615)——>效果图
73 |
74 |
75 |
76 | ### ● ActionBarToast.java——>顶部消息弹框——>[使用方法](http://blog.csdn.net/a_zhon/article/details/78988653)——>效果图
77 |
78 |
79 |
80 | ```
81 | ActionBarToast barToast = new ActionBarToast(this);
82 | barToast.showToast(ActionBarToast.LENGTH_SHORT);
83 | //barTost.cancel();
84 | ```
85 |
86 | ### ● MaskView.java——>观看视频开灯/关灯效果——>[使用方法](http://blog.csdn.net/a_zhon/article/details/78988653)——>效果图
87 |
88 |
89 |
90 | ```
91 | MaskView maskView = new MaskView(this);
92 | maskView.attachView(view);
93 | //maskView.removeMask();
94 | ```
95 |
96 | ### ● PhoneEditText.java——>手机号码输入框自动分割——>效果图
97 |
98 |
99 |
100 | ### ● CustomTouchView.java——>自定义滚动View——>效果图
101 |
102 |
103 |
--------------------------------------------------------------------------------
/ScratchCard.java:
--------------------------------------------------------------------------------
1 | package com.zsy.lambda;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.Paint;
9 | import android.graphics.Path;
10 | import android.graphics.PorterDuff;
11 | import android.graphics.PorterDuffXfermode;
12 | import android.util.AttributeSet;
13 | import android.view.MotionEvent;
14 | import android.view.View;
15 |
16 | /*
17 | * 项目名: AndroidHeros
18 | * 包名: com.zsy.androidheros.view
19 | * 文件名: ScratchCard
20 | * 创建者: ZSY
21 | * 创建时间: 2016/11/4 17:15
22 | * 描述: 刮刮卡效果
23 | */
24 | public class ScratchCard extends View {
25 |
26 | //手指触摸屏幕,
27 | private Paint paint;
28 | private Path path;
29 | //隐藏的背景图片
30 | private Bitmap bgBitmap;
31 | //覆盖在上层的灰色图层
32 | private Bitmap fgBitmap;
33 | private Canvas canvas;
34 |
35 | public ScratchCard(Context context) {
36 | super(context);
37 | initPaint();
38 | }
39 |
40 | public ScratchCard(Context context, AttributeSet attrs) {
41 | super(context, attrs);
42 | initPaint();
43 | }
44 |
45 | public ScratchCard(Context context, AttributeSet attrs, int defStyleAttr) {
46 | super(context, attrs, defStyleAttr);
47 | }
48 |
49 | private void initPaint() {
50 | paint = new Paint();
51 | paint.setAlpha(0);
52 | //在已有的图像上绘图将会在其上面添加一层新的图层,如果新图层的paint是不透明的,那么它将遮挡住下面的paint;
53 | //如果新图层它是部分透明的,那么它不透明的地方将会被染上下面的颜色
54 | paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
55 | paint.setStyle(Paint.Style.STROKE);
56 | //设置笔触和连接处能更加圆滑
57 | paint.setStrokeJoin(Paint.Join.ROUND);
58 | paint.setStrokeCap(Paint.Cap.ROUND);
59 |
60 | paint.setStrokeWidth(60);
61 | path = new Path();
62 | bgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.a_zhon);
63 | fgBitmap = Bitmap.createBitmap(bgBitmap.getWidth(), bgBitmap.getHeight(), Bitmap.Config.ARGB_8888);
64 | canvas = new Canvas(fgBitmap);
65 | //在图层上绘制一层颜色
66 | canvas.drawColor(Color.parseColor("#1dcdef"));
67 | }
68 |
69 | @Override
70 | protected void onDraw(Canvas canvas) {
71 | super.onDraw(canvas);
72 | //绘制背景图层
73 | canvas.drawBitmap(bgBitmap, 0, 0, null);
74 | //绘制遮罩图层
75 | canvas.drawBitmap(fgBitmap, 0, 0, null);
76 | }
77 |
78 | @Override
79 | public boolean onTouchEvent(MotionEvent event) {
80 | switch (event.getAction()) {
81 | case MotionEvent.ACTION_DOWN:
82 | path.reset();
83 | path.moveTo(event.getX(), event.getY());
84 | //实现点击檫除
85 | path.lineTo(event.getX() + 1, event.getY() + 1);
86 | break;
87 | case MotionEvent.ACTION_MOVE:
88 | path.lineTo(event.getX(), event.getY());
89 | break;
90 | case MotionEvent.ACTION_UP:
91 | break;
92 | }
93 | canvas.drawPath(path, paint);
94 | invalidate();
95 | return true;
96 |
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/ScreenSizeUtils.java:
--------------------------------------------------------------------------------
1 | package cn.hjtech.pigeon.common.utils;
2 |
3 | import android.content.Context;
4 | import android.util.DisplayMetrics;
5 | import android.view.WindowManager;
6 |
7 | /**
8 | * 文件名: SharePreUtils
9 | * 创建时间: 2017/05/24 on 17:58
10 | * 描述:
11 | * 1.获取手机屏幕的宽高
12 | * 2.dip 与 px 之间的转换
13 | *
14 | * @see android.support.annotation.Px
15 | */
16 |
17 | public class ScreenSizeUtils {
18 |
19 | private static ScreenSizeUtils instance = null;
20 | private int screenWidth, screenHeight;
21 |
22 | public static ScreenSizeUtils getInstance(Context mContext) {
23 | if (instance == null) {
24 | synchronized (ScreenSizeUtils.class) {
25 | if (instance == null)
26 | instance = new ScreenSizeUtils(mContext);
27 | }
28 | }
29 | return instance;
30 | }
31 |
32 | private ScreenSizeUtils(Context mContext) {
33 | WindowManager manager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
34 | DisplayMetrics dm = new DisplayMetrics();
35 | manager.getDefaultDisplay().getMetrics(dm);
36 | // 获取屏幕分辨率宽度
37 | screenWidth = dm.widthPixels;
38 | // 获取屏幕分辨率高度
39 | screenHeight = dm.heightPixels;
40 | }
41 |
42 | //获取屏幕宽度
43 | public int getScreenWidth() {
44 | return screenWidth;
45 | }
46 |
47 | //获取屏幕高度
48 | public int getScreenHeight() {
49 | return screenHeight;
50 | }
51 |
52 | /**
53 | * 根据手机的分辨率从 dip 的单位 转成为 px(像素)
54 | *
55 | * @return px
56 | */
57 | private int dip2px(Context context, float dpValue) {
58 | final float scale = context.getResources().getDisplayMetrics().density;
59 | return (int) (dpValue * scale + 0.5f);
60 | }
61 |
62 | /**
63 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
64 | */
65 | public static int px2dip(Context context, float pxValue) {
66 | final float scale = context.getResources().getDisplayMetrics().density;
67 | return (int) (pxValue / scale + 0.5f);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/SharePreUtils.java:
--------------------------------------------------------------------------------
1 | package cn.hjtech.pigeon.common.utils;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | import java.io.BufferedReader;
7 | import java.io.File;
8 | import java.io.FileInputStream;
9 | import java.io.InputStreamReader;
10 |
11 | /*
12 | * 文件名: SharePreUtils
13 | * 创建时间: 2017/05/24 on 11:58
14 | * 描述: SharedPreferences存取工具类
15 | */
16 |
17 | public class SharePreUtils {
18 |
19 | //配置文件,文件名
20 | public static final String SHARE_NAME = "config";
21 |
22 | /**
23 | * 万能put
24 | *
25 | * @param mContext 上下文
26 | * @param key 键
27 | * @param values 值
28 | */
29 | public static void put(Context mContext, String key, Object values) {
30 | if (values instanceof String) {
31 | putString(mContext, key, (String) values);
32 | } else if (values instanceof Integer) {
33 | putInt(mContext, key, (Integer) values);
34 | } else if (values instanceof Boolean) {
35 | putBoolean(mContext, key, (Boolean) values);
36 | } else {
37 | //除去上面三个类型,都存储为String
38 | putString(mContext, key, String.valueOf(values));
39 | }
40 |
41 | }
42 |
43 | /**
44 | * 存字符串
45 | *
46 | * @param mContext
47 | * @param key
48 | * @param values
49 | */
50 | public static void putString(Context mContext, String key, String values) {
51 | SharedPreferences sp = mContext.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
52 | sp.edit().putString(key, values).commit();
53 | }
54 |
55 |
56 | /**
57 | * 取字符串
58 | *
59 | * @param mContext 上下文
60 | * @param key 键
61 | * @param values 默认值
62 | * @return 取出的值
63 | */
64 | public static String getString(Context mContext, String key, String values) {
65 | SharedPreferences sp = mContext.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
66 | return sp.getString(key, values);
67 | }
68 |
69 |
70 | /**
71 | * 存布尔值
72 | *
73 | * @param mContext 上下文
74 | * @param key 键
75 | * @param values 值
76 | */
77 | public static void putBoolean(Context mContext, String key, boolean values) {
78 | SharedPreferences sp = mContext.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
79 | sp.edit().putBoolean(key, values).commit();
80 | }
81 |
82 | /**
83 | * 取布尔值
84 | *
85 | * @param mContext 上下文
86 | * @param key 键
87 | * @param values 默认值
88 | * @return true/false
89 | */
90 | public static boolean getBoolean(Context mContext, String key, boolean values) {
91 | SharedPreferences sp = mContext.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
92 | return sp.getBoolean(key, values);
93 | }
94 |
95 | /**
96 | * 存int值
97 | *
98 | * @param mContext 上下文
99 | * @param key 键
100 | * @param values 值
101 | */
102 | public static void putInt(Context mContext, String key, int values) {
103 | SharedPreferences sp = mContext.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
104 | sp.edit().putInt(key, values).commit();
105 | }
106 |
107 | /**
108 | * 取int值
109 | *
110 | * @param mContext 上下文
111 | * @param key 键
112 | * @param values 默认值
113 | * @return
114 | */
115 | public static int getInt(Context mContext, String key, int values) {
116 | SharedPreferences sp = mContext.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
117 | return sp.getInt(key, values);
118 | }
119 |
120 | /**
121 | * 删除一条字段
122 | *
123 | * @param mContext 上下文
124 | * @param key 键
125 | */
126 | public static void deleShare(Context mContext, String key) {
127 | SharedPreferences sp = mContext.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
128 | //单个清理
129 | sp.edit().remove(key).commit();
130 | }
131 |
132 | /**
133 | * 删除全部数据
134 | *
135 | * @param mContext 上下文
136 | */
137 | public static void deleShareAll(Context mContext) {
138 | SharedPreferences sp = mContext.getSharedPreferences(SHARE_NAME, Context.MODE_PRIVATE);
139 | //全部清理
140 | sp.edit().clear().commit();
141 | }
142 |
143 | /**
144 | * 查看SharedPreferences的内容
145 | */
146 | public static String lookSharePre(Context context) {
147 | try {
148 | FileInputStream stream = new FileInputStream(new File("/data/data/" + context.getPackageName() + "/shared_prefs", "config.xml"));
149 | BufferedReader bff = new BufferedReader(new InputStreamReader(stream));
150 | String line;
151 | StringBuilder sb = new StringBuilder();
152 | while ((line = bff.readLine()) != null) {
153 | sb.append(line);
154 | sb.append("\n");
155 | }
156 | return sb.toString();
157 | } catch (Exception e) {
158 | e.printStackTrace();
159 | }
160 | return "未找到当前配置文件!";
161 | }
162 | }
--------------------------------------------------------------------------------
/TimeClock.java:
--------------------------------------------------------------------------------
1 | package com.zsy.androidheros.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.util.AttributeSet;
8 | import android.view.View;
9 |
10 | import java.text.SimpleDateFormat;
11 | import java.util.Date;
12 |
13 | /*
14 | * 项目名: AndroidHeros
15 | * 包名: com.zsy.androidheros.view
16 | * 文件名: TimeClock
17 | * 创建者: 阿钟
18 | * 创建时间: 2016/11/3 12:54
19 | * 描述: 绘制时钟
20 | */
21 | public class TimeClock extends View {
22 |
23 | //外圆画笔
24 | private Paint paint;
25 | //文字画笔
26 | private Paint paintNum;
27 | //时钟画笔
28 | private Paint paintHour;
29 | //分钟画笔
30 | private Paint paintMinute;
31 | //秒钟画笔
32 | private Paint paintSecond;
33 | //外圆圆心
34 | private float x, y;
35 | //外圆半径
36 | private int r;
37 |
38 | public TimeClock(Context context) {
39 | super(context);
40 | initPaint();
41 | }
42 |
43 | public TimeClock(Context context, AttributeSet attrs) {
44 | super(context, attrs);
45 | initPaint();
46 | }
47 |
48 | public TimeClock(Context context, AttributeSet attrs, int defStyleAttr) {
49 | super(context, attrs, defStyleAttr);
50 | initPaint();
51 | }
52 |
53 |
54 | private void initPaint() {
55 | paint = new Paint();
56 | paint.setColor(Color.BLACK);
57 | paint.setAntiAlias(true);
58 | paint.setStrokeWidth(3);
59 | paint.setStyle(Paint.Style.STROKE);
60 |
61 | paintNum = new Paint();
62 | paintNum.setColor(Color.BLACK);
63 | paintNum.setAntiAlias(true);
64 | paintNum.setTextSize(35);
65 | paintNum.setStyle(Paint.Style.STROKE);
66 | paintNum.setTextAlign(Paint.Align.CENTER);
67 |
68 | paintSecond = new Paint();
69 | paintSecond.setColor(Color.RED);
70 | paintSecond.setAntiAlias(true);
71 | paintSecond.setStrokeWidth(5);
72 | paintSecond.setStyle(Paint.Style.FILL);
73 |
74 | paintMinute = new Paint();
75 | paintMinute.setColor(Color.BLACK);
76 | paintMinute.setAntiAlias(true);
77 | paintMinute.setStrokeWidth(8);
78 | paintMinute.setStyle(Paint.Style.FILL);
79 |
80 | paintHour = new Paint();
81 | paintHour.setColor(Color.BLACK);
82 | paintHour.setAntiAlias(true);
83 | paintHour.setStrokeWidth(13);
84 | paintHour.setStyle(Paint.Style.FILL);
85 | }
86 |
87 | @Override
88 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
89 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
90 | int height = getMeasuredHeight();
91 | int width = getMeasuredWidth();
92 | x = width / 2;
93 | y = height / 2;
94 | r = (int) x - 5;
95 | }
96 |
97 | @Override
98 | protected void onDraw(Canvas canvas) {
99 | super.onDraw(canvas);
100 | //绘制外圆
101 | canvas.drawCircle(x, y, r, paint);
102 |
103 | //绘制圆心
104 | canvas.drawCircle(x, y, 15, paintMinute);
105 |
106 | //绘制刻度
107 | drawLines(canvas);
108 |
109 | //绘制整点
110 | drawText(canvas);
111 |
112 | try {
113 | initCurrentTime(canvas);
114 | } catch (Exception e) {
115 | e.printStackTrace();
116 | }
117 | //每隔1s刷新界面
118 | postInvalidateDelayed(1000);
119 | }
120 |
121 |
122 | /**
123 | * 绘制时钟刻度和分钟刻度
124 | *
125 | * @param canvas 画布
126 | */
127 | private void drawLines(Canvas canvas) {
128 | for (int i = 0; i < 60; i++) {
129 | if (i % 5 == 0) {
130 | //绘制整点刻度
131 | paint.setStrokeWidth(8);
132 | canvas.drawLine(x, y - r, x, y - r + 40, paint);
133 | } else {
134 | //绘制分钟刻度
135 | paint.setStrokeWidth(3);
136 | canvas.drawLine(x, y - r, x, y - r + 30, paint);
137 | }
138 | //绕着(x,y)旋转6°
139 | canvas.rotate(6, x, y);
140 | }
141 | }
142 |
143 | /**
144 | * 绘制整点数字
145 | *
146 | * @param canvas 画布
147 | */
148 | private void drawText(Canvas canvas) {
149 | // 获取文字高度用于设置文本垂直居中
150 | float textSize = (paintNum.getFontMetrics().bottom - paintNum.getFontMetrics().top);
151 | // 数字离圆心的距离,40为刻度的长度,20文字大小
152 | int distance = r - 40 - 20;
153 | // 数字的坐标(a,b)
154 | float a, b;
155 | // 每30°写一个数字
156 | for (int i = 0; i < 12; i++) {
157 | a = (float) (distance * Math.sin(i * 30 * Math.PI / 180) + x);
158 | b = (float) (y - distance * Math.cos(i * 30 * Math.PI / 180));
159 | if (i == 0) {
160 | canvas.drawText("12", a, b + textSize / 3, paintNum);
161 | } else {
162 | canvas.drawText(String.valueOf(i), a, b + textSize / 3, paintNum);
163 | }
164 | }
165 | }
166 |
167 | /**
168 | * 获取当前系统时间
169 | *
170 | * @param canvas 画布
171 | */
172 | private void initCurrentTime(Canvas canvas) {
173 | //获取系统当前时间
174 | SimpleDateFormat format = new SimpleDateFormat("HH-mm-ss");
175 | String time = format.format(new Date(System.currentTimeMillis()));
176 | String[] split = time.split("-");
177 | int hour = Integer.parseInt(split[0]);
178 | int minute = Integer.parseInt(split[1]);
179 | int second = Integer.parseInt(split[2]);
180 | //时针走过的角度
181 | int hourAngle = hour * 30 + minute / 2;
182 | //分针走过的角度
183 | int minuteAngle = minute * 6 + second / 10;
184 | //秒针走过的角度
185 | int secondAngle = second * 6;
186 |
187 | //绘制时钟,以12整点为0°参照点
188 | canvas.rotate(hourAngle, x, y);
189 | canvas.drawLine(x, y, x, y - r + 150, paintHour);
190 | canvas.save();
191 | canvas.restore();
192 | //这里画好了时钟,我们需要再将画布转回来,继续以12整点为0°参照点
193 | canvas.rotate(-hourAngle, x, y);
194 |
195 | //绘制分钟
196 | canvas.rotate(minuteAngle, x, y);
197 | canvas.drawLine(x, y, x, y - r + 60, paintMinute);
198 | canvas.save();
199 | canvas.restore();
200 | //这里同上
201 | canvas.rotate(-minuteAngle, x, y);
202 |
203 | //绘制秒钟
204 | canvas.rotate(secondAngle, x, y);
205 | canvas.drawLine(x, y, x, y - r + 20, paintSecond);
206 | }
207 |
208 | }
209 |
--------------------------------------------------------------------------------
/TimeUtils.java:
--------------------------------------------------------------------------------
1 | package com.hjtech.baselib.utils;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.util.Date;
5 |
6 | /*
7 | * 文件名: TimeUtils
8 | * 创建者: 阿钟
9 | * 创建时间: 2017/8/22 on 15:12
10 | * 描述: TODO 时间格式化工具类
11 | */
12 | public class TimeUtils {
13 |
14 | /**
15 | * 将时间戳转换为时间
16 | *
17 | * @param time 时间戳
18 | * @return yyyy-MM-dd
19 | */
20 | public static String yyyyMMdd(long time) {
21 | if (time == 0)
22 | return "";
23 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
24 | Date date = new Date(time);
25 | return format.format(date);
26 | }
27 |
28 | /***
29 | * 时间戳转换为时间
30 | *
31 | * @param time 时间戳
32 | * @return HH:mm:ss
33 | */
34 | public static String HHmmss(long time) {
35 | if (time == 0)
36 | return "";
37 | SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
38 | Date date = new Date(time);
39 | return format.format(date);
40 | }
41 |
42 | /***
43 | * 时间戳转换为时间
44 | *
45 | * @param time 时间戳
46 | * @return yyyy-MM-dd HH:mm
47 | */
48 | public static String yyyyMMddHHmm(long time) {
49 | if (time == 0)
50 | return "";
51 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
52 | Date date = new Date(time);
53 | return format.format(date);
54 | }
55 |
56 | /***
57 | * 时间戳转换为时间
58 | *
59 | * @param time 时间戳
60 | * @return yyyy-MM-dd HH:mm:ss
61 | */
62 | public static String yyyyMMddHHmmss(long time) {
63 | if (time == 0)
64 | return "";
65 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
66 | Date date = new Date(time);
67 | return format.format(date);
68 | }
69 |
70 | /***
71 | * 时间戳转换为时间
72 | *
73 | * @param time 时间戳
74 | * @return MM-dd HH:mm
75 | */
76 | public static String MMddHHmm(long time) {
77 | if (time == 0) {
78 | return "";
79 | }
80 | SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm");
81 | Date date = new Date(time);
82 | return format.format(date);
83 | }
84 |
85 | /***
86 | * 时间戳转换为时间
87 | *
88 | * @param time 时间戳
89 | * @return MM-dd HH:mm:ss
90 | */
91 | public static String MMddHHmmss(long time) {
92 | if (time == 0) {
93 | return "";
94 | }
95 | SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss");
96 | Date date = new Date(time);
97 | return format.format(date);
98 | }
99 |
100 | /***
101 | * 时间转换为时间戳
102 | *
103 | * @param time 时间
104 | * @param format 格式 例如:yyyy-MM-dd HH:mm:ss
105 | * @return 时间戳
106 | */
107 | public static long toTimeStamp(String time, String format) {
108 | SimpleDateFormat format = new SimpleDateFormat(format);
109 | return format.parse(time).getTime();
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/WaveView.java:
--------------------------------------------------------------------------------
1 | package com.azhon.test;
2 |
3 | import android.animation.ValueAnimator;
4 | import android.content.Context;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.graphics.Path;
9 | import android.graphics.Point;
10 | import android.graphics.Rect;
11 | import android.support.annotation.Nullable;
12 | import android.util.AttributeSet;
13 | import android.view.View;
14 | import android.view.animation.LinearInterpolator;
15 |
16 | /*
17 | * 文件名: WaveView
18 | * 创建者: ZSY
19 | * 创建时间: 2017/8/29 on 09:10
20 | * 描述: TODO 水波纹view 剩余流量球
21 | */
22 | public class WaveView extends View {
23 |
24 | /*画布宽度*/
25 | private int width;
26 | /*画布高度*/
27 | private int height;
28 | /*sin曲线画笔*/
29 | private Paint paint;
30 | /*圆的画笔*/
31 | private Paint textPaint;
32 | /*文本画笔*/
33 | private Paint circlePaint;
34 | /*sin曲线的路径*/
35 | private Path path;
36 | /*sin曲线 1/4个周期的宽度*/
37 | private int cycle = 160;
38 | /*sin曲线振幅的高度*/
39 | private int waveHeight = 80;
40 | /*sin曲线的起点*/
41 | private Point startPoint;
42 | /*当前进度*/
43 | private int progress;
44 | /*x轴平移量*/
45 | private int translateX = 40;
46 | /*是否启用了动画设置进度*/
47 | private boolean openAnimate = false;
48 | /*是否自增长*/
49 | private boolean autoIncrement = true;
50 |
51 | public WaveView(Context context, @Nullable AttributeSet attrs) {
52 | super(context, attrs);
53 | init(context);
54 | }
55 |
56 | public WaveView(Context context) {
57 | super(context);
58 | init(context);
59 | }
60 |
61 | private void init(Context context) {
62 | path = new Path();
63 | paint = new Paint();
64 | paint.setAntiAlias(true);
65 | paint.setStrokeWidth(dip2px(context, 5));
66 | paint.setStyle(Paint.Style.FILL);
67 | paint.setColor(Color.GREEN);
68 |
69 | circlePaint = new Paint();
70 | circlePaint.setStrokeWidth(dip2px(context, 5));
71 | circlePaint.setStyle(Paint.Style.STROKE);
72 | circlePaint.setAntiAlias(true);
73 | circlePaint.setColor(Color.parseColor("#FF4081"));
74 |
75 | textPaint = new Paint();
76 | textPaint.setAntiAlias(true);
77 | textPaint.setTextSize(dip2px(context, 20));
78 | textPaint.setColor(Color.BLACK);
79 | }
80 |
81 | @Override
82 | protected void onDraw(Canvas canvas) {
83 | super.onDraw(canvas);
84 | //裁剪画布为圆形
85 | Path circlePath = new Path();
86 | circlePath.addCircle(width / 2, height / 2, width / 2, Path.Direction.CW);
87 | canvas.clipPath(circlePath);
88 | canvas.drawPaint(circlePaint);
89 | canvas.drawCircle(width / 2, height / 2, width / 2, circlePaint);
90 | //以下操作都是在这个圆形画布中操作
91 |
92 | //根据进度改变起点坐标的y值
93 | startPoint.y = (int) (height - (progress / 100.0 * height));
94 | //起点
95 | path.moveTo(startPoint.x, startPoint.y);
96 | int j = 1;
97 | //循环绘制正弦曲线 循环一次半个周期
98 | for (int i = 1; i <= 8; i++) {
99 | if (i % 2 == 0) {
100 | //波峰
101 | path.quadTo(startPoint.x + (cycle * j), startPoint.y + waveHeight,
102 | startPoint.x + (cycle * 2) * i, startPoint.y);
103 | } else {
104 | //波谷
105 | path.quadTo(startPoint.x + (cycle * j), startPoint.y - waveHeight,
106 | startPoint.x + (cycle * 2) * i, startPoint.y);
107 | }
108 | j += 2;
109 | }
110 | //绘制封闭的曲线
111 | path.lineTo(width, height);//右下角
112 | path.lineTo(startPoint.x, height);//左下角
113 | path.lineTo(startPoint.x, startPoint.y);//起点
114 | path.close();
115 | canvas.drawPath(path, paint);
116 |
117 | drawText(canvas, textPaint, progress + "%");
118 | //判断是不是平移完了一个周期
119 | if (startPoint.x + translateX >= 0) {
120 | //满了一个周期则恢复默认起点继续平移
121 | startPoint.x = -cycle * 4;
122 | }
123 | //每次波形的平移量 40
124 | startPoint.x += translateX;
125 | if (autoIncrement) {
126 | if (progress >= 100) {
127 | progress = 0;
128 | } else {
129 | progress++;
130 | }
131 | }
132 | path.reset();
133 | if (!openAnimate) {
134 | postInvalidateDelayed(150);
135 | }
136 | }
137 |
138 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
139 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
140 | //获取view的宽度
141 | width = getViewSize(400, widthMeasureSpec);
142 | //获取view的高度
143 | height = getViewSize(400, heightMeasureSpec);
144 | //默认从屏幕外先绘制3/4个周期 使得波峰在圆中间
145 | startPoint = new Point(-cycle * 3, height / 2);
146 | }
147 |
148 |
149 | private int getViewSize(int defaultSize, int measureSpec) {
150 | int viewSize = defaultSize;
151 | //获取测量模式
152 | int mode = MeasureSpec.getMode(measureSpec);
153 | //获取大小
154 | int size = MeasureSpec.getSize(measureSpec);
155 | switch (mode) {
156 | case MeasureSpec.UNSPECIFIED: //如果没有指定大小,就设置为默认大小
157 | viewSize = defaultSize;
158 | break;
159 | case MeasureSpec.AT_MOST: //如果测量模式是最大取值为size
160 | //我们将大小取最大值,你也可以取其他值
161 | viewSize = size;
162 | break;
163 | case MeasureSpec.EXACTLY: //如果是固定的大小,那就不要去改变它
164 | viewSize = size;
165 | break;
166 | }
167 | return viewSize;
168 | }
169 |
170 | /**
171 | * 绘制文字
172 | *
173 | * @param canvas 画布
174 | * @param paint 画笔
175 | * @param text 画的文字
176 | */
177 | private void drawText(Canvas canvas, Paint paint, String text) {
178 | //画布的大小
179 | Rect targetRect = new Rect(0, 0, width, height);
180 | Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
181 | int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
182 | // 下面这行是实现水平居中,drawText对应改为传入targetRect.centerX()
183 | paint.setTextAlign(Paint.Align.CENTER);
184 | canvas.drawText(text, targetRect.centerX(), baseline, paint);
185 | }
186 |
187 | /**
188 | * 根据手机的分辨率从 dip 的单位 转成为 px(像素)
189 | */
190 | public int dip2px(Context context, float dpValue) {
191 | final float scale = context.getResources().getDisplayMetrics().density;
192 | return (int) (dpValue * scale + 0.5f);
193 | }
194 |
195 | /**
196 | * 设置振幅高度
197 | *
198 | * @param waveHeight 振幅
199 | */
200 | public void setWaveHeight(int waveHeight) {
201 | this.waveHeight = waveHeight;
202 | invalidate();
203 | }
204 |
205 | /**
206 | * 设置sin曲线 1/4个周期的宽度
207 | *
208 | * @param cycle 1/4个周期的宽度
209 | */
210 | public void setCycle(int cycle) {
211 | this.cycle = cycle;
212 | invalidate();
213 | }
214 |
215 | /**
216 | * 设置当前进度
217 | *
218 | * @param progress 进度
219 | */
220 | public void setProgress(int progress) {
221 | if (progress > 100 || progress < 0)
222 | throw new RuntimeException(getClass().getName() + "请设置[0,100]之间的值");
223 | this.progress = progress;
224 | autoIncrement = false;
225 | invalidate();
226 | }
227 |
228 | /**
229 | * 设置x轴移动量
230 | *
231 | * @param translateX 默认40
232 | */
233 | public void setTranslateX(int translateX) {
234 | this.translateX = translateX;
235 | }
236 |
237 | /**
238 | * 通过动画设置当前进度
239 | *
240 | * @param progress 进度 <=100
241 | * @param duration 动画时长
242 | */
243 | public void setProgress(final int progress, int duration) {
244 | if (progress > 100 || progress < 0)
245 | throw new RuntimeException(getClass().getName() + "请设置[0,100]之间的值");
246 | autoIncrement = false;
247 | openAnimate = true;
248 | ValueAnimator progressAnimator = ValueAnimator.ofInt(0, progress);
249 | progressAnimator.setDuration(duration);
250 | progressAnimator.setTarget(progress);
251 | progressAnimator.setInterpolator(new LinearInterpolator());
252 | progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
253 | @Override
254 | public void onAnimationUpdate(ValueAnimator animation) {
255 | WaveView.this.progress = (int) animation.getAnimatedValue();
256 | if (WaveView.this.progress == progress)
257 | openAnimate = false;
258 | invalidate();
259 | }
260 | });
261 | progressAnimator.start();
262 | }
263 |
264 | public int getProgress() {
265 | return progress;
266 | }
267 | }
268 |
--------------------------------------------------------------------------------
/WordsNavigation.java:
--------------------------------------------------------------------------------
1 | package com.zsy.words.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Rect;
8 | import android.graphics.Typeface;
9 | import android.util.AttributeSet;
10 | import android.view.MotionEvent;
11 | import android.view.View;
12 |
13 | /*
14 | * 文件名: WordsNavigation
15 | * 创建者: ZSY
16 | * 创建时间: 2016/11/17 15:34
17 | * 描述: 实现手机联系人列表字母导航
18 | */
19 | public class WordsNavigation extends View {
20 |
21 | /*绘制的列表导航字母*/
22 | private String words[] = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
23 | "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "#"};
24 | /*字母画笔*/
25 | private Paint wordsPaint;
26 | /*字母背景画笔*/
27 | private Paint bgPaint;
28 | /*每一个字母的宽度*/
29 | private int itemWidth;
30 | /*每一个字母的高度*/
31 | private int itemHeight;
32 | /*手指按下的字母索引*/
33 | private int touchIndex = 0;
34 | /*手指按下的字母改变接口*/
35 | private onWordsChangeListener listener;
36 |
37 | public WordsNavigation(Context context) {
38 | super(context);
39 | init(context);
40 | }
41 |
42 | public WordsNavigation(Context context, AttributeSet attrs) {
43 | super(context, attrs);
44 | init(context);
45 | }
46 |
47 | /**
48 | * 初始化画笔
49 | */
50 | private void init(Context context) {
51 | wordsPaint = new Paint();
52 | wordsPaint.setColor(Color.parseColor("#F7F7F7"));
53 | wordsPaint.setAntiAlias(true);
54 | wordsPaint.setTextSize(dip2px(context,12));
55 | wordsPaint.setTypeface(Typeface.DEFAULT_BOLD);
56 |
57 | bgPaint = new Paint();
58 | bgPaint.setAntiAlias(true);
59 | bgPaint.setColor(Color.parseColor("#1dcdef"));
60 | }
61 |
62 | @Override
63 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
64 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
65 | itemWidth = getMeasuredWidth();
66 | //使得边距好看一些
67 | int height = getMeasuredHeight() - 10;
68 | itemHeight = height / 27;
69 | }
70 |
71 | @Override
72 | protected void onDraw(Canvas canvas) {
73 | super.onDraw(canvas);
74 | for (int i = 0; i < words.length; i++) {
75 | //判断是不是我们按下的当前字母
76 | if (touchIndex == i) {
77 | //绘制文字圆形背景
78 | canvas.drawCircle(itemWidth / 2, itemHeight / 2 + i * itemHeight, 23, bgPaint);
79 | wordsPaint.setColor(Color.WHITE);
80 | } else {
81 | wordsPaint.setColor(Color.GRAY);
82 | }
83 | //获取文字的宽高
84 | Rect rect = new Rect();
85 | wordsPaint.getTextBounds(words[i], 0, 1, rect);
86 | int wordWidth = rect.width();
87 | //绘制字母
88 | float wordX = itemWidth / 2 - wordWidth / 2;
89 | float wordY = itemWidth / 2 + i * itemHeight;
90 | canvas.drawText(words[i], wordX, wordY, wordsPaint);
91 | }
92 | }
93 |
94 | /**
95 | * 当手指触摸按下的时候改变字母背景颜色
96 | */
97 | @Override
98 | public boolean onTouchEvent(MotionEvent event) {
99 | switch (event.getAction()) {
100 | case MotionEvent.ACTION_DOWN:
101 | case MotionEvent.ACTION_MOVE:
102 | float y = event.getY();
103 | //获得我们按下的是那个索引(字母)
104 | int index = (int) (y / itemHeight);
105 | if (index != touchIndex)
106 | touchIndex = index;
107 | //防止数组越界
108 | if (listener != null && 0 <= touchIndex && touchIndex <= words.length - 1) {
109 | //回调按下的字母
110 | listener.wordsChange(words[touchIndex]);
111 | }
112 | invalidate();
113 | break;
114 | case MotionEvent.ACTION_UP:
115 | //手指抬起,不做任何操作
116 | break;
117 | }
118 | return true;
119 | }
120 |
121 | /**
122 | * 根据手机的分辨率从 dip 的单位 转成为 px(像素)
123 | */
124 | public int dip2px(Context context, float dpValue) {
125 | final float scale = context.getResources().getDisplayMetrics().density;
126 | return (int) (dpValue * scale + 0.5f);
127 | }
128 |
129 | /**
130 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
131 | */
132 | public int px2dip(Context context, float pxValue) {
133 | final float scale = context.getResources().getDisplayMetrics().density;
134 | return (int) (pxValue / scale + 0.5f);
135 | }
136 |
137 | /*设置当前按下的是那个字母*/
138 | public void setTouchIndex(String word) {
139 | for (int i = 0; i < words.length; i++) {
140 | if (words[i].equals(word)) {
141 | touchIndex = i;
142 | invalidate();
143 | return;
144 | }
145 | }
146 | }
147 |
148 | /*手指按下了哪个字母的回调接口*/
149 | public interface onWordsChangeListener {
150 | void wordsChange(String words);
151 | }
152 |
153 | /*设置手指按下字母改变监听*/
154 | public void setOnWordsChangeListener(onWordsChangeListener listener) {
155 | this.listener = listener;
156 | }
157 | }
158 |
159 |
--------------------------------------------------------------------------------
/effectImage/20161105231805323.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azhon/MyUtils/60b1f5357e276f694b089d2cc09d60ac59b637df/effectImage/20161105231805323.gif
--------------------------------------------------------------------------------
/effectImage/20161118122207199.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azhon/MyUtils/60b1f5357e276f694b089d2cc09d60ac59b637df/effectImage/20161118122207199.png
--------------------------------------------------------------------------------
/effectImage/custom_touch_view.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/azhon/MyUtils/60b1f5357e276f694b089d2cc09d60ac59b637df/effectImage/custom_touch_view.gif
--------------------------------------------------------------------------------