android自定义View-垂直滚动的TextView
来源:互联网 发布:hadoop书籍 知乎 编辑:程序博客网 时间:2024/04/30 10:39
自定义垂直滚动的TextView,主要是用来学习Scroller的使用。关于ScrollTextView的实现,可以看下面的介绍和源码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package com.orgcent.demo.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller;
import android.widget.TextView;
/**
* 配置android:scrollbars="vertical",启用垂直滚动条
*
* 实现水平滚动就是修改mScrollX,可参考HorizontalScrollView
*
* 滚动原理: 在绘制前对画布进行偏移操作
*
* 下面是View的绘制机制:
* |- view.computeScroll() --用来对mScrollX/Y进行修改。由于在绘制前调用,可调用invalite()来触发
* |- canvas.translate(-mScrollX,-mScrollY) --偏移画布
* |- view.draw() --绘制
*
* 上述内容可以在View.buildDrawingCache()或ViewGroup.dispatchDraw()->drawChild()中找到.直接查看方法名即可
*
* 滚动帮助类:
* Scroller --用来计算滚动后的偏移值.具体请参考ScrollView和HorizontalScrollView
* VelocityTracker --速度计算类。根据fling时的按下、抬起动作,计算滚动初速度
*
* ScrollTextView--流程解析:
* 1、onTouchEvent() --使用Scroller来计算滚动偏移值
* 2、重写computeScroll() --对View的mScrollY进行修改, 此处控制滚动范围
*
* 滚动范围:
* 最小值:0
* 最大值:所有文本高度+内边距-View高度。也就是超出屏幕的文本高度
*/
public class ScrollTextView extends TextView {
private Scroller mScroller;
private int mTouchSlop;
private int mMinimumVelocity;
private int mMaximumVelocity;
private float mLastMotionY;
private boolean mIsBeingDragged;
private VelocityTracker mVelocityTracker;
private int mActivePointerId= INVALID_POINTER;
private static final int INVALID_POINTER =-1;
public ScrollTextView(Context context,AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
initView();
}
public ScrollTextView(Context context,AttributeSet attrs) {
super(context, attrs);
initView();
}
public ScrollTextView(Context context){
super(context);
initView();
}
private void initView(){
final Context cx = getContext();
//设置滚动减速器,在fling中会用到
mScroller = new Scroller(cx,new DecelerateInterpolator(0.5f));
final ViewConfiguration configuration = ViewConfiguration.get(cx);
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
}
/**
* 此方法为最后机会来修改mScrollX,mScrollY.
* 这方法后将根据mScrollX,mScrollY来偏移Canvas已实现内容滚动
*/
@Override
public void computeScroll(){
super.computeScroll();
final Scroller scroller = mScroller;
if(scroller.computeScrollOffset()){ //正在滚动,让view滚动到当前位置
int scrollY = scroller.getCurrY();
final int maxY = (getLineCount()* getLineHeight()+ getPaddingTop()+ getPaddingBottom())- getHeight();
boolean toEdge = scrollY< 0 || scrollY> maxY;
if(scrollY < 0)
scrollY = 0;
else if(scrollY> maxY)
scrollY = maxY;
/*
*下面等同于:
* mScrollY = scrollY;
* awakenScrollBars(); //显示滚动条,必须在xml中配置。
* postInvalidate();
*/
scrollTo(0, scrollY);
if(toEdge)//移到两端,由于位置没有发生变化,导致滚动条不显示
awakenScrollBars();
}
}
public void fling(int velocityY){
final int maxY = (getLineCount()* getLineHeight()+ getPaddingTop()+ getPaddingBottom())- getHeight();
mScroller.fling(getScrollX(), getScrollY(),0, velocityY, 0, 0, 0,
Math.max(0, maxY));
//刷新,让父控件调用computeScroll()
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent ev){
/*
* 事件处理方式:先自己处理后交给父类处理。
* PS:方式不同,可能导致效果不同。请根据需求自行修改。
*/
boolean handled = false;
final int contentHeight = getLineCount() * getLineHeight();
if(contentHeight > getHeight()){
handled = processScroll(ev);
}
return handled | super.onTouchEvent(ev);
}
private boolean processScroll(MotionEvent ev){
boolean handled = false;
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);//帮助类,用来在fling时计算移动初速度
final int action = ev.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:{
if(!mScroller.isFinished()){
mScroller.forceFinished(true);
}
mLastMotionY = ev.getY();
mActivePointerId = ev.getPointerId(0);
mIsBeingDragged = true;
handled = true;
break;
}
case MotionEvent.ACTION_MOVE:{
final int pointerId = mActivePointerId;
if(mIsBeingDragged && INVALID_POINTER != pointerId){
final int pointerIndex = ev.findPointerIndex(pointerId);
final float y = ev.getY(pointerIndex);
int deltaY = (int) (mLastMotionY - y);
if(Math.abs(deltaY)> mTouchSlop) { //移动距离(正负代表方向)必须大于ViewConfiguration设置的默认值
mLastMotionY = y;
/*
* 默认滚动时间为250ms,建议立即滚动,否则滚动效果不明显
* 或者直接使用scrollBy(0, deltaY);
*/
mScroller.startScroll(getScrollX(), getScrollY(),0, deltaY, 0);
invalidate();
handled = true;
}
}
break;
}
case MotionEvent.ACTION_UP:{
final int pointerId = mActivePointerId;
if(mIsBeingDragged && INVALID_POINTER != pointerId){
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity(pointerId);
if(Math.abs(initialVelocity)> mMinimumVelocity) {
fling(-initialVelocity);
}
mActivePointerId = INVALID_POINTER;
mIsBeingDragged = false;
if (mVelocityTracker!= null){
mVelocityTracker.recycle();
mVelocityTracker = null;
}
handled = true;
}
break;
}
}
return handled;
}
}
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.animation.DecelerateInterpolator;
import android.widget.Scroller;
import android.widget.TextView;
/**
* 配置android:scrollbars="vertical",启用垂直滚动条
*
* 实现水平滚动就是修改mScrollX,可参考HorizontalScrollView
*
* 滚动原理: 在绘制前对画布进行偏移操作
*
* 下面是View的绘制机制:
* |- view.computeScroll() --用来对mScrollX/Y进行修改。由于在绘制前调用,可调用invalite()来触发
* |- canvas.translate(-mScrollX,-mScrollY) --偏移画布
* |- view.draw() --绘制
*
* 上述内容可以在View.buildDrawingCache()或ViewGroup.dispatchDraw()->drawChild()中找到.直接查看方法名即可
*
* 滚动帮助类:
* Scroller --用来计算滚动后的偏移值.具体请参考ScrollView和HorizontalScrollView
* VelocityTracker --速度计算类。根据fling时的按下、抬起动作,计算滚动初速度
*
* ScrollTextView--流程解析:
* 1、onTouchEvent() --使用Scroller来计算滚动偏移值
* 2、重写computeScroll() --对View的mScrollY进行修改, 此处控制滚动范围
*
* 滚动范围:
* 最小值:0
* 最大值:所有文本高度+内边距-View高度。也就是超出屏幕的文本高度
*/
public class ScrollTextView extends TextView {
private Scroller mScroller;
private int mTouchSlop;
private int mMinimumVelocity;
private int mMaximumVelocity;
private float mLastMotionY;
private boolean mIsBeingDragged;
private VelocityTracker mVelocityTracker;
private int mActivePointerId= INVALID_POINTER;
private static final int INVALID_POINTER =-1;
public ScrollTextView(Context context,AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
initView();
}
public ScrollTextView(Context context,AttributeSet attrs) {
super(context, attrs);
initView();
}
public ScrollTextView(Context context){
super(context);
initView();
}
private void initView(){
final Context cx = getContext();
//设置滚动减速器,在fling中会用到
mScroller = new Scroller(cx,new DecelerateInterpolator(0.5f));
final ViewConfiguration configuration = ViewConfiguration.get(cx);
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
}
/**
* 此方法为最后机会来修改mScrollX,mScrollY.
* 这方法后将根据mScrollX,mScrollY来偏移Canvas已实现内容滚动
*/
@Override
public void computeScroll(){
super.computeScroll();
final Scroller scroller = mScroller;
if(scroller.computeScrollOffset()){ //正在滚动,让view滚动到当前位置
int scrollY = scroller.getCurrY();
final int maxY = (getLineCount()* getLineHeight()+ getPaddingTop()+ getPaddingBottom())- getHeight();
boolean toEdge = scrollY< 0 || scrollY> maxY;
if(scrollY < 0)
scrollY = 0;
else if(scrollY> maxY)
scrollY = maxY;
/*
*下面等同于:
* mScrollY = scrollY;
* awakenScrollBars(); //显示滚动条,必须在xml中配置。
* postInvalidate();
*/
scrollTo(0, scrollY);
if(toEdge)//移到两端,由于位置没有发生变化,导致滚动条不显示
awakenScrollBars();
}
}
public void fling(int velocityY){
final int maxY = (getLineCount()* getLineHeight()+ getPaddingTop()+ getPaddingBottom())- getHeight();
mScroller.fling(getScrollX(), getScrollY(),0, velocityY, 0, 0, 0,
Math.max(0, maxY));
//刷新,让父控件调用computeScroll()
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent ev){
/*
* 事件处理方式:先自己处理后交给父类处理。
* PS:方式不同,可能导致效果不同。请根据需求自行修改。
*/
boolean handled = false;
final int contentHeight = getLineCount() * getLineHeight();
if(contentHeight > getHeight()){
handled = processScroll(ev);
}
return handled | super.onTouchEvent(ev);
}
private boolean processScroll(MotionEvent ev){
boolean handled = false;
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);//帮助类,用来在fling时计算移动初速度
final int action = ev.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:{
if(!mScroller.isFinished()){
mScroller.forceFinished(true);
}
mLastMotionY = ev.getY();
mActivePointerId = ev.getPointerId(0);
mIsBeingDragged = true;
handled = true;
break;
}
case MotionEvent.ACTION_MOVE:{
final int pointerId = mActivePointerId;
if(mIsBeingDragged && INVALID_POINTER != pointerId){
final int pointerIndex = ev.findPointerIndex(pointerId);
final float y = ev.getY(pointerIndex);
int deltaY = (int) (mLastMotionY - y);
if(Math.abs(deltaY)> mTouchSlop) { //移动距离(正负代表方向)必须大于ViewConfiguration设置的默认值
mLastMotionY = y;
/*
* 默认滚动时间为250ms,建议立即滚动,否则滚动效果不明显
* 或者直接使用scrollBy(0, deltaY);
*/
mScroller.startScroll(getScrollX(), getScrollY(),0, deltaY, 0);
invalidate();
handled = true;
}
}
break;
}
case MotionEvent.ACTION_UP:{
final int pointerId = mActivePointerId;
if(mIsBeingDragged && INVALID_POINTER != pointerId){
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity(pointerId);
if(Math.abs(initialVelocity)> mMinimumVelocity) {
fling(-initialVelocity);
}
mActivePointerId = INVALID_POINTER;
mIsBeingDragged = false;
if (mVelocityTracker!= null){
mVelocityTracker.recycle();
mVelocityTracker = null;
}
handled = true;
}
break;
}
}
return handled;
}
}
测试DEMO: 点击查看
- android自定义View-垂直滚动的TextView
- android自定义View-垂直滚动的TextView
- android自定义View-垂直滚动的TextView
- 自定义View-垂直滚动的TextView
- 自定义View之垂直滚动TextView
- android TextView 的垂直滚动
- android自定义View之垂直的滚动条
- [Android] TextView的垂直滚动效果
- Android 垂直自动滚动的textview
- [Android] TextView的垂直滚动效果
- Android TextView的垂直滚动效果
- Android TextView的垂直滚动效果
- Android-垂直上下滚动的TextView
- 自定义垂直滚动切换TextView
- 垂直滚动textview自定义实现
- TextView 的垂直滚动
- Android 自定义控件:垂直滚动的TextView实现竖向跑马灯效果
- Android:TextView的垂直滚动效果,和上下滚动效果
- 也谈WebKit、Gecko使用图形库
- 面试100题:8.杂题(先占位)
- Js操作Select大全
- sqlserver数据库 数据格式化
- some websites
- android自定义View-垂直滚动的TextView
- 解决vista和win7在windows服务中交互桌面权限问题:穿透Session 0 隔离
- DynamicSkinForm VCL 12.8支持Win8
- __stdcall,_cdecl,__fastcall,PASCAL 的调用方式
- 判断两个表结构及其数据是否相等
- 可编辑listView controls
- 用到ajax的页面后台输出一段文字
- Android控制ScrollView滑动速度
- 面试100题:9.判断整数序列是不是二元查找树的后序遍历结果