码迷,mamicode.com
首页 > 其他好文 > 详细

3.自定义控件(三)

时间:2015-12-05 19:15:18      阅读:183      评论:0      收藏:0      [点我收藏+]

标签:

继承已有ViewGroup实现自定义控件
技术分享
模拟ViewPager的效果:
实现步骤:
1、自定义view继承viewGroup。
2、重写onLayout方法,为每一个子View确定位置。
3、重写onTouchEvent方法,监听touch事件,并用scrollTo()或scrollBy()方法移动view,
4、监听UP事件,当手指抬起时,判断应显示的页面位置,并计算距离、滑动页面。
5、添加页面切换的监听事件。

布局
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:gravity="center_horizontal"
  6. android:orientation="vertical"
  7. tools:context=".MainActivity" >
  8. <RadioGroup
  9. android:id="@+id/radioGroup"
  10. android:layout_width="match_parent"
  11. android:layout_height="wrap_content"
  12. android:orientation="horizontal" >
  13. </RadioGroup>
  14. <com.itheima.myscrollview28.MyScrollView
  15. android:id="@+id/myscroll_view"
  16. android:layout_width="match_parent"
  17. android:layout_height="match_parent" />
  18. </LinearLayout>
还有一个页面随便添加一些控件,listview,放在中间,来练习onTouchEvent事件传递
MyScroller:计算位移距离的工具类,这个是匀速运动
new Scroller(ctx);//这个是系统的,自定义起的方法名都和它起的一样,能快速切换
  1. public class MyScroller {

  2. private int startX;
  3. private int startY;
  4. private int distanceX;
  5. private int distanceY;
  6. /**
  7. * 开始执行动画的时间
  8. */
  9. private long startTime;
  10. /**
  11. * 判断是否正在执行动画
  12. * true 是还在运行
  13. * false 已经停止
  14. */
  15. private boolean isFinish;
  16. public MyScroller(Context ctx){
  17. }
  18. /**
  19. * 开移移动:将这些信息记录下来
  20. * @param startX 开始时的X坐标
  21. * @param startY 开始时的Y坐标
  22. * @param disX X方向 要移动的距离
  23. * @param disY Y方向 要移动的距离
  24. */
  25. public void startScroll(int startX, int startY, int disX, int disY) {
  26. this.startX = startX;
  27. this.startY = startY;
  28. this.distanceX = disX;
  29. this.distanceY = disY;
  30. this.startTime = SystemClock.uptimeMillis();
  31. this.isFinish = false;
  32. }
  33. /**
  34. * 默认运行的时间
  35. * 毫秒值
  36. */
  37. private int duration = 500;
  38. /**
  39. * 当前的X值
  40. */
  41. private long currX;
  42. /**
  43. * 当前的Y值
  44. */
  45. private long currY;
  46. /**
  47. * 计算一下当前的运行状况
  48. * 返回值:
  49. * true 还在运行
  50. * false 运行结束
  51. */
  52. public boolean computeScrollOffset() {
  53. if (isFinish) {
  54. return false;
  55. }
  56. // 获得所用的时间
  57. long passTime = SystemClock.uptimeMillis() - startTime;
  58. // 如果时间还在允许的范围内
  59. if (passTime < duration) {
  60. // 当前的位置 = 开始的位置 + 移动的距离(距离 = 速度*时间)
  61. currX = startX + distanceX * passTime / duration;
  62. currY = startY + distanceY * passTime / duration;
  63. } else {
  64. currX = startX + distanceX;
  65. currY = startY + distanceY;
  66. isFinish = true;
  67. }
  68. return true;
  69. }
  70. public long getCurrX() {
  71. return currX;
  72. }
  73. public void setCurrX(long currX) {
  74. this.currX = currX;
  75. }
  76. }
MyScrollView:手势识别器其实就是onTouchEvent提供的简单工具类
技术分享
  1. public class MyScrollView extends ViewGroup{
  2. private Context ctx;
  3. /**
  4. * 判断是否发生快速滑动
  5. */
  6. protected boolean isFling;
  7. public MyScrollView(Context context, AttributeSet attrs) {
  8. super(context, attrs);
  9. // TODO Auto-generated constructor stub
  10. this.ctx = context;
  11. initView();
  12. }
  13. private void initView() {
  14. // myScroller = new MyScroller(ctx);
  15. myScroller = new Scroller(ctx);//这个是系统的,自定义的方法名都和它起的的一样
  16. detector = new GestureDetector(ctx, new OnGestureListener() {
  17. @Override
  18. public boolean onSingleTapUp(MotionEvent e) {
  19. return false;
  20. }
  21. @Override
  22. public void onShowPress(MotionEvent e) {
  23. }
  24. @Override
  25. /**
  26. * 响应手指在屏幕上的滑动事件
  27. */
  28. public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
  29. float distanceY) {
  30. //移动屏幕
  31. // System.out.println("distanceX::"+distanceX);
  32. /**
  33. * 移动当前view内容 移动一段距离
  34. * disX X方向移的距离 为正是,图片向左移动,为负时,图片向右移动
  35. * disY Y方向移动的距离
  36. */
  37. scrollBy((int) distanceX, 0);
  38. /**
  39. * 将当前视图的基准点移动到某个点 坐标点
  40. * x 水平方向X坐标
  41. * Y 竖直方向Y坐标
  42. * scrollTo(x, y);
  43. */
  44. return false;
  45. }
  46. @Override
  47. public void onLongPress(MotionEvent e) {
  48. }
  49. @Override
  50. /**
  51. * 发生快速滑动时的回调
  52. */
  53. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
  54. float velocityY) {
  55. isFling = true;
  56. if(velocityX>0 && currId>0){ // 快速向右滑动
  57. currId--;
  58. }else if(velocityX<0 && currId<getChildCount()-1){ // 快速向左滑动
  59. currId++;
  60. }
  61. moveToDest(currId);
  62. return false;
  63. }
  64. @Override
  65. public boolean onDown(MotionEvent e) {
  66. return false;
  67. }
  68. });
  69. }
  70. @Override
  71. /**
  72. * 计算 控件大小,
  73. * 做为viewGroup 还有一个责任,,:计算 子view的大小
  74. */
  75. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  76. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  77. int size = MeasureSpec.getSize(widthMeasureSpec);
  78. int mode = MeasureSpec.getMode(widthMeasureSpec);
  79. for (int i = 0; i < getChildCount(); i++) {
  80. View v = getChildAt(i);
  81. v.measure(widthMeasureSpec, heightMeasureSpec);
  82. // v.getMeasuredWidth() // 得到测量的大小
  83. }
  84. }
  85. @Override
  86. /**
  87. * 对子view进行布局,确定子view的位置
  88. * changed 若为true ,说明布局发生了变化
  89. * l\t\r\b\ 是指当前viewgroup 在其父view中的位置
  90. */
  91. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  92. for (int i = 0; i < getChildCount(); i++) {
  93. View view = getChildAt(i); // 取得下标为I的子view
  94. /**
  95. * 父view 会根据子view的需求,和自身的情况,来综合确定子view的位置,(确定他的大小)
  96. */
  97. //指定子view的位置 , 左,上,右,下,是指在viewGround坐标系中的位置
  98. view.layout(0+i*getWidth(), 0, getWidth()+i*getWidth(), getHeight());
  99. // view.getWidth(); 得到view的真实的大小。
  100. }
  101. }
  102. /**
  103. * 手势识别的工具类
  104. */
  105. private GestureDetector detector;
  106. /**
  107. * 当前的ID值
  108. * 显示在屏幕上的子View的下标
  109. */
  110. private int currId = 0;
  111. /**
  112. * down 事件时的x坐标
  113. */
  114. private int firstX = 0;
  115. @Override
  116. public boolean onTouchEvent(MotionEvent event) {
  117. super.onTouchEvent(event);
  118. detector.onTouchEvent(event);
  119. //添加自己的事件解析
  120. switch (event.getAction()) {
  121. case MotionEvent.ACTION_DOWN:
  122. firstX = (int) event.getX();
  123. break;
  124. case MotionEvent.ACTION_MOVE:
  125. break;
  126. case MotionEvent.ACTION_UP:
  127. if(!isFling){// 在没有发生快速滑动的时候,才执行按位置判断currid
  128. int nextId = 0;
  129. if(event.getX()-firstX>getWidth()/2){ // 手指向右滑动,超过屏幕的1/2 当前的currid - 1
  130. nextId = currId-1;
  131. }else if(firstX - event.getX()>getWidth()/2){ // 手指向左滑动,超过屏幕的1/2 当前的currid + 1
  132. nextId = currId+1;
  133. }else{
  134. nextId = currId;
  135. }
  136. moveToDest(nextId);
  137. // scrollTo(0, 0);
  138. }
  139. isFling = false;
  140. break;
  141. }
  142. return true;
  143. }
  144. /**
  145. * 计算位移的工具类
  146. */
  147. // private MyScroller myScroller;
  148. private Scroller myScroller;
  149. /**
  150. * 移动到指定的屏幕上
  151. * @param nextId 屏幕 的下标
  152. */
  153. public void moveToDest(int nextId) {
  154. /*
  155. * 对 nextId 进行判断 ,确保 是在合理的范围
  156. * 即 nextId >=0 && next <=getChildCount()-1
  157. */
  158. //确保 currId>=0
  159. currId = (nextId>=0)?nextId:0;
  160. //确保 currId<=getChildCount()-1
  161. currId = (nextId<=getChildCount()-1)?nextId:(getChildCount()-1);
  162. //瞬间移动
  163. // scrollTo(currId*getWidth(), 0);
  164. //触发listener事件
  165. if(pageChangedListener!=null){
  166. pageChangedListener.moveToDest(currId);
  167. }
  168. int distance = currId*getWidth() - getScrollX(); // 最终的位置 - 现在的位置 = 要移动的距离
  169. // myScroller.startScroll(getScrollX(),0,distance,0);
  170. //设置运行的时间
  171. myScroller.startScroll(getScrollX(),0,distance,0,Math.abs(distance));
  172. /*
  173. * 刷新当前view onDraw()方法 的执行
  174. */
  175. invalidate();
  176. }
  177. @Override
  178. /**
  179. * invalidate(); 会导致 computeScroll()这个方法的执行
  180. */
  181. public void computeScroll() {
  182. if(myScroller.computeScrollOffset()){
  183. int newX = (int) myScroller.getCurrX();
  184. System.out.println("newX::"+newX);
  185. scrollTo(newX, 0);
  186. invalidate();
  187. };
  188. }
  189. public MyPageChangedListener getPageChangedListener() {
  190. return pageChangedListener;
  191. }
  192. public void setPageChangedListener(MyPageChangedListener pageChangedListener) {
  193. this.pageChangedListener = pageChangedListener;
  194. }
  195. private MyPageChangedListener pageChangedListener;
  196. /**
  197. * 页面改时时的监听接口
  198. * @author leo
  199. *
  200. */
  201. public interface MyPageChangedListener{
  202. void moveToDest(int currid);
  203. }
  204. }
使用手势识别器GestureDetector

技术分享
/**
* 设置引导页的基类, 不需要在清单文件中注册, 因为不需要界面展示
* 让其他Activity继承它就行
* @author Kevin
*
*/
public abstract class BaseSetupActivity extends Activity {
 
private GestureDetector mDectector;
public SharedPreferences mPref;
 
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPref = getSharedPreferences("config", MODE_PRIVATE);
 
// 手势识别器
mDectector = new GestureDetector(this, new SimpleOnGestureListener() {
 
/**
* 监听手势滑动事件 e1表示滑动的起点,e2表示滑动终点 velocityX表示水平速度 velocityY表示垂直速度
*/
@Override
public 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();
}

 
@Override
public boolean onTouchEvent(MotionEvent event) {
mDectector.onTouchEvent(event);// 委托手势识别器处理触摸事件
return super.onTouchEvent(event);
}
 
}
一定要及得委托手势识别器,否则上边的代码没用

onTouchEvent
更多看智慧北京-滑动事件处理
技术分享
技术分享





3.自定义控件(三)

标签:

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!