[关闭]
@linux1s1s 2019-02-14T17:44:34.000000Z 字数 6461 阅读 2187

Android 控件开发初步

AndroidWidget 2015-05


了解Android组件开发之前需要先对Android View有个大体的了解,建议看一下博客: Android View 分析(上)Android View 分析(中)Android View 分析(下),这三篇博客对View从头到尾分析了一遍,虽然没有涉及更深的层次,但是应付组件开发应该够了,这篇博客是组件开发的起步,我们先来说一下比较简单的组件开发。

手动绘制 onDraw + invalidate

我们知道在Android View 分析(下)中,如果调用invalidate方法会引起重绘,也就是invalidate(...) --> onDraw(...), 而如果想手动绘制控件,那么只需要在合适的时机调用invalidate(...)方法引起重绘,然后在onDraw(...)方法中绘制你定制的内容即可,看下面的Demo

  1. public class CounterView extends View implements OnClickListener {
  2. private Paint mPaint;
  3. private Rect mBounds;
  4. private int mCount;
  5. public CounterView(Context context, AttributeSet attrs) {
  6. super(context, attrs);
  7. //直接在构造器中获取画笔
  8. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  9. mBounds = new Rect();
  10. setOnClickListener(this);
  11. }
  12. @Override
  13. protected void onDraw(Canvas canvas) {
  14. super.onDraw(canvas); //重写onDraw方法
  15. mPaint.setColor(Color.BLUE); //设置画笔颜色
  16. canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint); //画一个长方形
  17. mPaint.setColor(Color.YELLOW); //重设画笔颜色
  18. mPaint.setTextSize(30); //设置文本字体大小
  19. String text = String.valueOf(mCount);
  20. mPaint.getTextBounds(text, 0, text.length(), mBounds);
  21. float textWidth = mBounds.width();
  22. float textHeight = mBounds.height(); //获取字体高宽
  23. canvas.drawText(text, getWidth() / 2 - textWidth / 2, getHeight() / 2 + textHeight / 2, mPaint); //画文本,注意这个文本的位置。
  24. }
  25. @Override
  26. public void onClick(View v) {
  27. mCount++;
  28. invalidate(); //在合适的时机调用invalidate()引发重绘
  29. }
  30. }

接下来看看XML文件

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent" >
  4. <com.example.customview.CounterView
  5. android:layout_width="100dp"
  6. android:layout_height="100dp"
  7. android:layout_centerInParent="true" />
  8. </RelativeLayout>

看一下运行结果:
此处输入图片的描述
需要每次点击一下View才能更新计数,当然这里可以自己写一个线程不停的更新计数。

组合控件

这一种方式的自定义最没有技术含量,如果仅仅是将通用的View控件组合在一起的话,当然如果App中这样的组件出现的比较频繁,可以组合这样一种组件,以供App使用,同时也方便维护。

考虑到App中出现比较多这样的组件

此处输入图片的描述

那么可以将这个组件封装一下,暂且取个名字TitleView,接下来让我们来完成这个TitileView

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="50dp"
  5. android:background="#ffcb05" >
  6. <Button
  7. android:id="@+id/button_left"
  8. android:layout_width="60dp"
  9. android:layout_height="40dp"
  10. android:layout_centerVertical="true"
  11. android:layout_marginLeft="5dp"
  12. android:background="@drawable/back_button"
  13. android:text="Back"
  14. android:textColor="#fff" />
  15. <TextView
  16. android:id="@+id/title_text"
  17. android:layout_width="wrap_content"
  18. android:layout_height="wrap_content"
  19. android:layout_centerInParent="true"
  20. android:text="This is Title"
  21. android:textColor="#fff"
  22. android:textSize="20sp" />
  23. </RelativeLayout>
  1. public class TitleView extends FrameLayout {
  2. private Button leftButton;
  3. private TextView titleText;
  4. public TitleView(Context context, AttributeSet attrs) {
  5. super(context, attrs);
  6. LayoutInflater.from(context).inflate(R.layout.title, this);
  7. titleText = (TextView) findViewById(R.id.title_text);
  8. leftButton = (Button) findViewById(R.id.button_left);
  9. leftButton.setOnClickListener(new OnClickListener() {
  10. @Override
  11. public void onClick(View v) {
  12. ((Activity) getContext()).finish();
  13. }
  14. });
  15. }
  16. public void setTitleText(String text) {
  17. titleText.setText(text);
  18. }
  19. public void setLeftButtonText(String text) {
  20. leftButton.setText(text);
  21. }
  22. public void setLeftButtonListener(OnClickListener l) {
  23. leftButton.setOnClickListener(l);
  24. }
  25. }

下面我们来看看如何使用上面定义好的TitleView

  1. <RelativeLayout 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. <com.example.customview.TitleView
  6. android:id="@+id/title_view"
  7. android:layout_width="match_parent"
  8. android:layout_height="wrap_content" >
  9. </com.example.customview.TitleView>
  10. </RelativeLayout>

扩展控件

这里主要是对现有的控件进行扩展,比如现在有个需求,对现有的ListView进行扩展,增加单个Item滑动提供删除功能。
我们打算手动加载删除Button,当然也可以在Item的XML中直接添加这个Button,然后检测手势。然后决定是否显示还是隐藏删除Button。这个检测手势的操作如下

  1. //首先生成一个GestureDetector的实例
  2. gestureDetector = new GestureDetector(getContext(), this);
  3. //然后判断X轴的偏移量是不是大于Y轴的偏移量,如果条件满足表示左右滑动,否则表示上下滑动
  4. if (Math.abs(velocityX) > Math.abs(velocityY))
  5. {
  6. ...
  7. }
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Button xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:id="@+id/delete_button"
  4. android:layout_width="wrap_content"
  5. android:layout_height="wrap_content"
  6. android:background="@drawable/delete_button" >
  7. </Button>
  1. public class MyListView extends ListView implements OnTouchListener,
  2. OnGestureListener {
  3. private GestureDetector gestureDetector;
  4. private OnDeleteListener listener;
  5. private View deleteButton;
  6. private ViewGroup itemLayout;
  7. private int selectedItem;
  8. private boolean isDeleteShown;
  9. public MyListView(Context context, AttributeSet attrs) {
  10. super(context, attrs);
  11. gestureDetector = new GestureDetector(getContext(), this);
  12. setOnTouchListener(this);
  13. }
  14. public void setOnDeleteListener(OnDeleteListener l) {
  15. listener = l;
  16. }
  17. @Override
  18. public boolean onTouch(View v, MotionEvent event) {
  19. if (isDeleteShown) {
  20. itemLayout.removeView(deleteButton);
  21. deleteButton = null;
  22. isDeleteShown = false;
  23. return false;
  24. } else {
  25. return gestureDetector.onTouchEvent(event);
  26. }
  27. }
  28. @Override
  29. public boolean onDown(MotionEvent e) {
  30. if (!isDeleteShown) {
  31. selectedItem = pointToPosition((int) e.getX(), (int) e.getY());
  32. }
  33. return false;
  34. }
  35. @Override
  36. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
  37. float velocityY) {
  38. if (!isDeleteShown && Math.abs(velocityX) > Math.abs(velocityY)) {
  39. deleteButton = LayoutInflater.from(getContext()).inflate(
  40. R.layout.delete_button, null);
  41. deleteButton.setOnClickListener(new OnClickListener() {
  42. @Override
  43. public void onClick(View v) {
  44. itemLayout.removeView(deleteButton);
  45. deleteButton = null;
  46. isDeleteShown = false;
  47. listener.onDelete(selectedItem);
  48. }
  49. });
  50. itemLayout = (ViewGroup) getChildAt(selectedItem
  51. - getFirstVisiblePosition());
  52. RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
  53. LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
  54. params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
  55. params.addRule(RelativeLayout.CENTER_VERTICAL);
  56. itemLayout.addView(deleteButton, params);
  57. isDeleteShown = true;
  58. }
  59. return false;
  60. }
  61. @Override
  62. public boolean onSingleTapUp(MotionEvent e) {
  63. return false;
  64. }
  65. @Override
  66. public void onShowPress(MotionEvent e) {
  67. }
  68. @Override
  69. public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
  70. float distanceY) {
  71. return false;
  72. }
  73. @Override
  74. public void onLongPress(MotionEvent e) {
  75. }
  76. public interface OnDeleteListener {
  77. void onDelete(int index);
  78. }
  79. }

这里在MyListView的构造方法中创建了一个GestureDetector的实例用于监听手势,然后给MyListView注册了touch监听事件。然后在onTouch()方法中进行判断,如果删除按钮已经显示了,就将它移除掉,如果删除按钮没有显示,就使用GestureDetector来处理当前手势。

当手指按下时,会调用OnGestureListener的onDown()方法,在这里通过pointToPosition()方法来判断出当前选中的是ListView的哪一行。当手指快速滑动时,会调用onFling()方法,在这里会去加载delete_button.xml这个布局,然后将删除按钮添加到当前选中的那一行item上。注意,我们还给删除按钮添加了一个点击事件,当点击了删除按钮时就会回调onDeleteListener的onDelete()方法,在回调方法中应该去处理具体的删除操作。

最后看一下运行结果:

此处输入图片的描述

本文转载并做了适当修改:http://blog.csdn.net/guolin_blog/article/details/17357967

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注