标签:
继承已有ViewGroup实现自定义控件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_horizontal"android:orientation="vertical"tools:context=".MainActivity" ><RadioGroupandroid:id="@+id/radioGroup"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal" ></RadioGroup><com.itheima.myscrollview28.MyScrollViewandroid:id="@+id/myscroll_view"android:layout_width="match_parent"android:layout_height="match_parent" /></LinearLayout>
public class MyScroller {private int startX;private int startY;private int distanceX;private int distanceY;/*** 开始执行动画的时间*/private long startTime;/*** 判断是否正在执行动画* true 是还在运行* false 已经停止*/private boolean isFinish;public MyScroller(Context ctx){}/*** 开移移动:将这些信息记录下来* @param startX 开始时的X坐标* @param startY 开始时的Y坐标* @param disX X方向 要移动的距离* @param disY Y方向 要移动的距离*/public void startScroll(int startX, int startY, int disX, int disY) {this.startX = startX;this.startY = startY;this.distanceX = disX;this.distanceY = disY;this.startTime = SystemClock.uptimeMillis();this.isFinish = false;}/*** 默认运行的时间* 毫秒值*/private int duration = 500;/*** 当前的X值*/private long currX;/*** 当前的Y值*/private long currY;/*** 计算一下当前的运行状况* 返回值:* true 还在运行* false 运行结束*/public boolean computeScrollOffset() {if (isFinish) {return false;}// 获得所用的时间long passTime = SystemClock.uptimeMillis() - startTime;// 如果时间还在允许的范围内if (passTime < duration) {// 当前的位置 = 开始的位置 + 移动的距离(距离 = 速度*时间)currX = startX + distanceX * passTime / duration;currY = startY + distanceY * passTime / duration;} else {currX = startX + distanceX;currY = startY + distanceY;isFinish = true;}return true;}public long getCurrX() {return currX;}public void setCurrX(long currX) {this.currX = currX;}}
public class MyScrollView extends ViewGroup{private Context ctx;/*** 判断是否发生快速滑动*/protected boolean isFling;public MyScrollView(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stubthis.ctx = context;initView();}private void initView() {// myScroller = new MyScroller(ctx);myScroller = new Scroller(ctx);//这个是系统的,自定义的方法名都和它起的的一样detector = new GestureDetector(ctx, new OnGestureListener() {@Overridepublic boolean onSingleTapUp(MotionEvent e) {return false;}@Overridepublic void onShowPress(MotionEvent e) {}@Override/*** 响应手指在屏幕上的滑动事件*/public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,float distanceY) {//移动屏幕// System.out.println("distanceX::"+distanceX);/*** 移动当前view内容 移动一段距离* disX X方向移的距离 为正是,图片向左移动,为负时,图片向右移动* disY Y方向移动的距离*/scrollBy((int) distanceX, 0);/*** 将当前视图的基准点移动到某个点 坐标点* x 水平方向X坐标* Y 竖直方向Y坐标* scrollTo(x, y);*/return false;}@Overridepublic void onLongPress(MotionEvent e) {}@Override/*** 发生快速滑动时的回调*/public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {isFling = true;if(velocityX>0 && currId>0){ // 快速向右滑动currId--;}else if(velocityX<0 && currId<getChildCount()-1){ // 快速向左滑动currId++;}moveToDest(currId);return false;}@Overridepublic boolean onDown(MotionEvent e) {return false;}});}@Override/*** 计算 控件大小,* 做为viewGroup 还有一个责任,,:计算 子view的大小*/protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int size = MeasureSpec.getSize(widthMeasureSpec);int mode = MeasureSpec.getMode(widthMeasureSpec);for (int i = 0; i < getChildCount(); i++) {View v = getChildAt(i);v.measure(widthMeasureSpec, heightMeasureSpec);// v.getMeasuredWidth() // 得到测量的大小}}@Override/*** 对子view进行布局,确定子view的位置* changed 若为true ,说明布局发生了变化* l\t\r\b\ 是指当前viewgroup 在其父view中的位置*/protected void onLayout(boolean changed, int l, int t, int r, int b) {for (int i = 0; i < getChildCount(); i++) {View view = getChildAt(i); // 取得下标为I的子view/*** 父view 会根据子view的需求,和自身的情况,来综合确定子view的位置,(确定他的大小)*///指定子view的位置 , 左,上,右,下,是指在viewGround坐标系中的位置view.layout(0+i*getWidth(), 0, getWidth()+i*getWidth(), getHeight());// view.getWidth(); 得到view的真实的大小。}}/*** 手势识别的工具类*/private GestureDetector detector;/*** 当前的ID值* 显示在屏幕上的子View的下标*/private int currId = 0;/*** down 事件时的x坐标*/private int firstX = 0;@Overridepublic boolean onTouchEvent(MotionEvent event) {super.onTouchEvent(event);detector.onTouchEvent(event);//添加自己的事件解析switch (event.getAction()) {case MotionEvent.ACTION_DOWN:firstX = (int) event.getX();break;case MotionEvent.ACTION_MOVE:break;case MotionEvent.ACTION_UP:if(!isFling){// 在没有发生快速滑动的时候,才执行按位置判断curridint nextId = 0;if(event.getX()-firstX>getWidth()/2){ // 手指向右滑动,超过屏幕的1/2 当前的currid - 1nextId = currId-1;}else if(firstX - event.getX()>getWidth()/2){ // 手指向左滑动,超过屏幕的1/2 当前的currid + 1nextId = currId+1;}else{nextId = currId;}moveToDest(nextId);// scrollTo(0, 0);}isFling = false;break;}return true;}/*** 计算位移的工具类*/// private MyScroller myScroller;private Scroller myScroller;/*** 移动到指定的屏幕上* @param nextId 屏幕 的下标*/public void moveToDest(int nextId) {/** 对 nextId 进行判断 ,确保 是在合理的范围* 即 nextId >=0 && next <=getChildCount()-1*///确保 currId>=0currId = (nextId>=0)?nextId:0;//确保 currId<=getChildCount()-1currId = (nextId<=getChildCount()-1)?nextId:(getChildCount()-1);//瞬间移动// scrollTo(currId*getWidth(), 0);//触发listener事件if(pageChangedListener!=null){pageChangedListener.moveToDest(currId);}int distance = currId*getWidth() - getScrollX(); // 最终的位置 - 现在的位置 = 要移动的距离// myScroller.startScroll(getScrollX(),0,distance,0);//设置运行的时间myScroller.startScroll(getScrollX(),0,distance,0,Math.abs(distance));/** 刷新当前view onDraw()方法 的执行*/invalidate();}@Override/*** invalidate(); 会导致 computeScroll()这个方法的执行*/public void computeScroll() {if(myScroller.computeScrollOffset()){int newX = (int) myScroller.getCurrX();System.out.println("newX::"+newX);scrollTo(newX, 0);invalidate();};}public MyPageChangedListener getPageChangedListener() {return pageChangedListener;}public void setPageChangedListener(MyPageChangedListener pageChangedListener) {this.pageChangedListener = pageChangedListener;}private MyPageChangedListener pageChangedListener;/*** 页面改时时的监听接口* @author leo**/public interface MyPageChangedListener{void moveToDest(int currid);}}

/*** 设置引导页的基类, 不需要在清单文件中注册, 因为不需要界面展示* 让其他Activity继承它就行* @author Kevin**/public abstract class BaseSetupActivity extends Activity {private GestureDetector mDectector;public SharedPreferences mPref;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mPref = getSharedPreferences("config", MODE_PRIVATE);// 手势识别器mDectector = new GestureDetector(this, new SimpleOnGestureListener() {/*** 监听手势滑动事件 e1表示滑动的起点,e2表示滑动终点 velocityX表示水平速度 velocityY表示垂直速度*/@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2,float velocityX, float velocityY) {// 判断纵向滑动幅度是否过大, 过大的话不允许切换界面if (Math.abs(e2.getRawY() - e1.getRawY()) > 100) {Toast.makeText(BaseSetupActivity.this, "不能这样划哦!",Toast.LENGTH_SHORT).show();return true;}// 判断滑动是否过慢if (Math.abs(velocityX) < 100) {Toast.makeText(BaseSetupActivity.this, "滑动的太慢了!",Toast.LENGTH_SHORT).show();return true;}// 向右划,上一页if (e2.getRawX() - e1.getRawX() > 200) {showPreviousPage();return true;}// 向左划, 下一页if (e1.getRawX() - e2.getRawX() > 200) {showNextPage();return true;}return super.onFling(e1, e2, velocityX, velocityY);}});}/*** 展示下一页, 子类必须实现,比如动画的逻辑可以写在这里*/public abstract void showNextPage();/*** 展示上一页, 子类必须实现*/public abstract void showPreviousPage();//几乎每个页面都有按钮,所以把按钮监听放到这里,代码会简化很多// 点击下一页按钮public void next(View view) {showNextPage();}// 点击上一页按钮public void previous(View view) {showPreviousPage();}@Overridepublic boolean onTouchEvent(MotionEvent event) {mDectector.onTouchEvent(event);// 委托手势识别器处理触摸事件return super.onTouchEvent(event);}}

标签:
原文地址:http://www.cnblogs.com/liuyu0529/p/5021904.html