android自定义View-垂直滚动的TextView
From:http://orgcent.com/android-custom-vertical-scroll-textview/
其实要让TextView能够滚动,可以使用ScrollView/HorizontalScrollView或者设置ScrollingMovementMethod来实现。
点击查看:android实现TextView垂直或水平滚动
下面自定义垂直滚动的TextView,主要是用来学习Scroller的使用。关于ScrollTextView的实现,可以看下面的介绍和源码。
packagecom.orgcent.demo.view;
importandroid.content.Context;
importandroid.util.AttributeSet;
importandroid.view.MotionEvent;
importandroid.view.VelocityTracker;
importandroid.view.ViewConfiguration;
importandroid.view.animation.DecelerateInterpolator;
importandroid.widget.Scroller;
importandroid.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高度。也就是超出屏幕的文本高度
*/
publicclassScrollTextViewextendsTextView{
privateScroller mScroller;
privateintmTouchSlop;
privateintmMinimumVelocity;
privateintmMaximumVelocity;
privatefloatmLastMotionY;
privatebooleanmIsBeingDragged;
privateVelocityTracker mVelocityTracker;
privateintmActivePointerId=INVALID_POINTER;
privatestaticfinalintINVALID_POINTER=-1;
publicScrollTextView(Contextcontext,AttributeSetattrs,intdefStyle){
super(context, attrs, defStyle);
initView();
}
publicScrollTextView(Contextcontext,AttributeSetattrs){
super(context, attrs);
initView();
}
publicScrollTextView(Contextcontext){
super(context);
initView();
}
privatevoidinitView(){
finalContextcx=getContext();
//设置滚动减速器,在fling中会用到
mScroller=newScroller(cx,newDecelerateInterpolator(0.5f));
finalViewConfiguration configuration=ViewConfiguration.get(cx);
mTouchSlop=configuration.getScaledTouchSlop();
mMinimumVelocity=configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity=configuration.getScaledMaximumFlingVelocity();
}
/**
* 此方法为最后机会来修改mScrollX,mScrollY.
* 这方法后将根据mScrollX,mScrollY来偏移Canvas已实现内容滚动
*/
@Override
publicvoidcomputeScroll(){
super.computeScroll();
finalScroller scroller=mScroller;
if(scroller.computeScrollOffset()){//正在滚动,让view滚动到当前位置
intscrollY=scroller.getCurrY();
finalintmaxY=(getLineCount()*getLineHeight()+getPaddingTop()+getPaddingBottom())-getHeight();
booleantoEdge=scrollY<0||scrollY>maxY;
if(scrollY<0)
scrollY=0;
elseif(scrollY>maxY)
scrollY=maxY;
/*
*下面等同于:
* mScrollY = scrollY;
* awakenScrollBars(); //显示滚动条,必须在xml中配置。
* postInvalidate();
*/
scrollTo(0, scrollY);
if(toEdge)//移到两端,由于位置没有发生变化,导致滚动条不显示
awakenScrollBars();
}
}
publicvoidfling(intvelocityY){
finalintmaxY=(getLineCount()*getLineHeight()+getPaddingTop()+getPaddingBottom())-getHeight();
mScroller.fling(getScrollX(), getScrollY(),0, velocityY,0,0,0,
Math.max(0, maxY));
//刷新,让父控件调用computeScroll()
invalidate();
}
@Override
publicbooleanonTouchEvent(MotionEvent ev){
/*
* 事件处理方式:先自己处理后交给父类处理。
* PS:方式不同,可能导致效果不同。请根据需求自行修改。
*/
booleanhandled=false;
finalintcontentHeight=getLineCount()*getLineHeight();
if(contentHeight>getHeight()){
handled=processScroll(ev);
}
returnhandled|super.onTouchEvent(ev);
}
privatebooleanprocessScroll(MotionEvent ev){
booleanhandled=false;
if(mVelocityTracker==null){
mVelocityTracker=VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);//帮助类,用来在fling时计算移动初速度
finalintaction=ev.getAction();
switch(action){
caseMotionEvent.ACTION_DOWN:{
if(!mScroller.isFinished()){
mScroller.forceFinished(true);
}
mLastMotionY=ev.getY();
mActivePointerId=ev.getPointerId(0);
mIsBeingDragged=true;
handled=true;
break;
}
caseMotionEvent.ACTION_MOVE:{
finalintpointerId=mActivePointerId;
if(mIsBeingDragged&&INVALID_POINTER!=pointerId){
finalintpointerIndex=ev.findPointerIndex(pointerId);
finalfloaty=ev.getY(pointerIndex);
intdeltaY=(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;
}
caseMotionEvent.ACTION_UP:{
finalintpointerId=mActivePointerId;
if(mIsBeingDragged&&INVALID_POINTER!=pointerId){
finalVelocityTracker velocityTracker=mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
intinitialVelocity=(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;
}
}
returnhandled;
}
}
更多相关文章
- Android麦克风录音带音量大小动态显示的圆形自定义View
- PopupWindow简单实现
- Android(安卓)OpenGL 文本显示 LabelMaker
- Android(安卓)Studio中TextView文本过长滚动显示方法
- Android实现图片 高斯模糊,以及图片镜像 翻转。
- android开发之修改ListView默认滑动条样式
- android源码中常用的Rect方法
- Android(安卓)列表使用(ListView GridView Gallery图片计时滚动)
- FrameLayout布局绘制流程解析