zoukankan      html  css  js  c++  java
  • Android自定义View的三种实现方式

     

      在毕设项目中多处用到自定义控件,一直打算总结一下自定义控件的实现方式,今天就来总结一下吧。在此之前学习了郭霖大神博客上面关于自定义View的几篇博文,感觉受益良多,本文中就参考了其中的一些内容。

      总结来说,自定义控件的实现有三种方式,分别是:组合控件、自绘控件和继承控件。下面将分别对这三种方式进行介绍。

    (一)组合控件

      组合控件,顾名思义就是将一些小的控件组合起来形成一个新的控件,这些小的控件多是系统自带的控件。比如很多应用中普遍使用的标题栏控件,其实用的就是组合控件,那么下面将通过实现一个简单的标题栏自定义控件来说说组合控件的用法。

    1、新建一个Android项目,创建自定义标题栏的布局文件title_bar.xml:

    复制代码
     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="wrap_content"
     5     android:background="#0000ff" >
     6 
     7     <Button
     8         android:id="@+id/left_btn"
     9         android:layout_width="wrap_content"
    10         android:layout_height="wrap_content"
    11         android:layout_centerVertical="true"
    12         android:layout_margin="5dp"
    13         android:background="@drawable/back1_64" />
    14 
    15     <TextView
    16         android:id="@+id/title_tv"
    17         android:layout_width="wrap_content"
    18         android:layout_height="wrap_content"
    19         android:layout_centerInParent="true"
    20         android:text="这是标题"
    21         android:textColor="#ffffff"
    22         android:textSize="20sp" />
    23 
    24 </RelativeLayout>
    复制代码

      可见这个标题栏控件还是比较简单的,其中在左边有一个返回按钮,背景是一张事先准备好的图片back1_64.png,标题栏中间是标题文字。

    2、创建一个类TitleView,继承自RelativeLayout:

    复制代码
     1 public class TitleView extends RelativeLayout {
     2 
     3     // 返回按钮控件
     4     private Button mLeftBtn;
     5     // 标题Tv
     6     private TextView mTitleTv;
     7 
     8     public TitleView(Context context, AttributeSet attrs) {
     9         super(context, attrs);
    10 
    11         // 加载布局
    12         LayoutInflater.from(context).inflate(R.layout.title_bar, this);
    13 
    14         // 获取控件
    15         mLeftBtn = (Button) findViewById(R.id.left_btn);
    16         mTitleTv = (TextView) findViewById(R.id.title_tv);
    17 
    18     }
    19 
    20     // 为左侧返回按钮添加自定义点击事件
    21     public void setLeftButtonListener(OnClickListener listener) {
    22         mLeftBtn.setOnClickListener(listener);
    23     }
    24 
    25     // 设置标题的方法
    26     public void setTitleText(String title) {
    27         mTitleTv.setText(title);
    28     }
    29 }
    复制代码

      在TitleView中主要是为自定义的标题栏加载了布局,为返回按钮添加事件监听方法,并提供了设置标题文本的方法。

    3、在activity_main.xml中引入自定义的标题栏:

    复制代码
     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     android:id="@+id/main_layout"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical" >
     6 
     7     <com.example.test.TitleView
     8         android:id="@+id/title_bar"
     9         android:layout_width="match_parent"
    10         android:layout_height="wrap_content" >
    11     </com.example.test.TitleView>
    12 
    13 </LinearLayout>
    复制代码

    4、在MainActivity中获取自定义的标题栏,并且为返回按钮添加自定义点击事件:

    复制代码
     1      private TitleView mTitleBar;
     2      mTitleBar = (TitleView) findViewById(R.id.title_bar);
     3 
     4         mTitleBar.setLeftButtonListener(new OnClickListener() {
     5 
     6             @Override
     7             public void onClick(View v) {
     8                 Toast.makeText(MainActivity.this, "点击了返回按钮", Toast.LENGTH_SHORT)
     9                         .show();
    10                 finish();
    11             }
    12         });
    复制代码

    5、运行效果如下:

      

      这样就用组合的方式实现了自定义标题栏,其实经过更多的组合还可以创建出功能更为复杂的自定义控件,比如自定义搜索栏等。

    (二)自绘控件

      自绘控件的内容都是自己绘制出来的,在View的onDraw方法中完成绘制。下面就实现一个简单的计数器,每点击它一次,计数值就加1并显示出来。

    1、创建CounterView类,继承自View,实现OnClickListener接口:

    复制代码
     1 public class CounterView extends View implements OnClickListener {
     2 
     3     // 定义画笔
     4     private Paint mPaint;
     5     // 用于获取文字的宽和高
     6     private Rect mBounds;
     7     // 计数值,每点击一次本控件,其值增加1
     8     private int mCount;
     9 
    10     public CounterView(Context context, AttributeSet attrs) {
    11         super(context, attrs);
    12 
    13         // 初始化画笔、Rect
    14         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    15         mBounds = new Rect();
    16         // 本控件的点击事件
    17         setOnClickListener(this);
    18     }
    19 
    20     @Override
    21     protected void onDraw(Canvas canvas) {
    22         super.onDraw(canvas);
    23 
    24         mPaint.setColor(Color.BLUE);
    25         // 绘制一个填充色为蓝色的矩形
    26         canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
    27 
    28         mPaint.setColor(Color.YELLOW);
    29         mPaint.setTextSize(50);
    30         String text = String.valueOf(mCount);
    31         // 获取文字的宽和高
    32         mPaint.getTextBounds(text, 0, text.length(), mBounds);
    33         float textWidth = mBounds.width();
    34         float textHeight = mBounds.height();
    35 
    36         // 绘制字符串
    37         canvas.drawText(text, getWidth() / 2 - textWidth / 2, getHeight() / 2
    38                 + textHeight / 2, mPaint);
    39     }
    40 
    41     @Override
    42     public void onClick(View v) {
    43         mCount ++;
    44         
    45         // 重绘
    46         invalidate();
    47     }
    48 
    49 }
    复制代码

    2、在activity_main.xml中引入该自定义布局:

    复制代码
     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     android:id="@+id/main_layout"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical" >
     6 
     7     <com.example.test.CounterView
     8         android:id="@+id/counter_view"
     9         android:layout_width="100dp"
    10         android:layout_height="100dp"
    11         android:layout_gravity="center_horizontal|top"
    12         android:layout_margin="20dp" />
    13 
    14 </LinearLayout>
    复制代码

    3、运行效果如下:

    (三)继承控件

      就是继承已有的控件,创建新控件,保留继承的父控件的特性,并且还可以引入新特性。下面就以支持横向滑动删除列表项的自定义ListView的实现来介绍。

    1、创建删除按钮布局delete_btn.xml,这个布局是在横向滑动列表项后显示的:

    复制代码
     1 <?xml version="1.0" encoding="utf-8"?>
     2 <Button xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:layout_width="wrap_content"
     4     android:layout_height="wrap_content"
     5     android:background="#FF0000"
     6     android:padding="5dp"
     7     android:text="删除"
     8     android:textColor="#FFFFFF"
     9     android:textSize="16sp" >
    10 
    11 </Button>
    复制代码

    2、创建CustomListView类,继承自ListView,并实现了OnTouchListener和OnGestureListener接口:

    复制代码
      1 public class CustomListView extends ListView implements OnTouchListener,
      2         OnGestureListener {
      3 
      4     // 手势动作探测器
      5     private GestureDetector mGestureDetector;
      6 
      7     // 删除事件监听器
      8     public interface OnDeleteListener {
      9         void onDelete(int index);
     10     }
     11 
     12     private OnDeleteListener mOnDeleteListener;
     13 
     14     // 删除按钮
     15     private View mDeleteBtn;
     16 
     17     // 列表项布局
     18     private ViewGroup mItemLayout;
     19 
     20     // 选择的列表项
     21     private int mSelectedItem;
     22 
     23     // 当前删除按钮是否显示出来了
     24     private boolean isDeleteShown;
     25 
     26     public CustomListView(Context context, AttributeSet attrs) {
     27         super(context, attrs);
     28 
     29         // 创建手势监听器对象
     30         mGestureDetector = new GestureDetector(getContext(), this);
     31 
     32         // 监听onTouch事件
     33         setOnTouchListener(this);
     34     }
     35 
     36     // 设置删除监听事件
     37     public void setOnDeleteListener(OnDeleteListener listener) {
     38         mOnDeleteListener = listener;
     39     }
     40 
     41     // 触摸监听事件
     42     @Override
     43     public boolean onTouch(View v, MotionEvent event) {
     44         if (isDeleteShown) {
     45             hideDelete();
     46             return false;
     47         } else {
     48             return mGestureDetector.onTouchEvent(event);
     49         }
     50     }
     51 
     52     @Override
     53     public boolean onDown(MotionEvent e) {
     54         if (!isDeleteShown) {
     55             mSelectedItem = pointToPosition((int) e.getX(), (int) e.getY());
     56         }
     57         return false;
     58     }
     59 
     60     @Override
     61     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
     62             float velocityY) {
     63         // 如果当前删除按钮没有显示出来,并且x方向滑动的速度大于y方向的滑动速度
     64         if (!isDeleteShown && Math.abs(velocityX) > Math.abs(velocityY)) {
     65             mDeleteBtn = LayoutInflater.from(getContext()).inflate(
     66                     R.layout.delete_btn, null);
     67 
     68             mDeleteBtn.setOnClickListener(new OnClickListener() {
     69 
     70                 @Override
     71                 public void onClick(View v) {
     72                     mItemLayout.removeView(mDeleteBtn);
     73                     mDeleteBtn = null;
     74                     isDeleteShown = false;
     75                     mOnDeleteListener.onDelete(mSelectedItem);
     76                 }
     77             });
     78 
     79             mItemLayout = (ViewGroup) getChildAt(mSelectedItem
     80                     - getFirstVisiblePosition());
     81 
     82             RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
     83                     LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
     84             params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
     85             params.addRule(RelativeLayout.CENTER_VERTICAL);
     86 
     87             mItemLayout.addView(mDeleteBtn, params);
     88             isDeleteShown = true;
     89         }
     90 
     91         return false;
     92     }
     93 
     94     // 隐藏删除按钮
     95     public void hideDelete() {
     96         mItemLayout.removeView(mDeleteBtn);
     97         mDeleteBtn = null;
     98         isDeleteShown = false;
     99     }
    100 
    101     public boolean isDeleteShown() {
    102         return isDeleteShown;
    103     }
    104     
    105     /**
    106      * 后面几个方法本例中没有用到
    107      */
    108     @Override
    109     public void onShowPress(MotionEvent e) {
    110 
    111     }
    112 
    113     @Override
    114     public boolean onSingleTapUp(MotionEvent e) {
    115         return false;
    116     }
    117 
    118     @Override
    119     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
    120             float distanceY) {
    121         return false;
    122     }
    123 
    124     @Override
    125     public void onLongPress(MotionEvent e) {
    126 
    127     }
    128 
    129 }
    复制代码

    3、定义列表项布局custom_listview_item.xml,它的结构很简单,只包含了一个TextView:

    复制代码
     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="match_parent"
     5     android:descendantFocusability="blocksDescendants" >
     6 
     7     <TextView
     8         android:id="@+id/content_tv"
     9         android:layout_width="wrap_content"
    10         android:layout_height="wrap_content"
    11         android:layout_centerVertical="true"
    12         android:layout_margin="30dp"
    13         android:gravity="center_vertical|left" />
    14 
    15 </RelativeLayout>
    复制代码

    4、定义适配器类CustomListViewAdapter,继承自ArrayAdapter<String>:

    复制代码
     1 public class CustomListViewAdapter extends ArrayAdapter<String> {
     2 
     3     public CustomListViewAdapter(Context context, int textViewResourceId,
     4             List<String> objects) {
     5         super(context, textViewResourceId, objects);
     6     }
     7 
     8     @Override
     9     public View getView(int position, View convertView, ViewGroup parent) {
    10         View view;
    11 
    12         if (convertView == null) {
    13             view = LayoutInflater.from(getContext()).inflate(
    14                     R.layout.custom_listview_item, null);
    15         } else {
    16             view = convertView;
    17         }
    18 
    19         TextView contentTv = (TextView) view.findViewById(R.id.content_tv);
    20         contentTv.setText(getItem(position));
    21 
    22         return view;
    23     }
    24 
    25 }
    复制代码

    5、在activity_main.xml中引入自定义的ListView:

    复制代码
     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     android:id="@+id/main_layout"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical" >
     6 
     7     <com.example.test.CustomListView
     8         android:id="@+id/custom_lv"
     9         android:layout_width="match_parent"
    10         android:layout_height="wrap_content" />
    11 
    12 </LinearLayout>
    复制代码

    6、在MainActivity中对列表做初始化、设置列表项删除按钮点击事件等处理:

    复制代码
     1 public class MainActivity extends Activity {
     2 
     3     // 自定义Lv
     4     private CustomListView mCustomLv;
     5     // 自定义适配器
     6     private CustomListViewAdapter mAdapter;
     7     // 内容列表
     8     private List<String> contentList = new ArrayList<String>();
     9 
    10     @Override
    11     protected void onCreate(Bundle savedInstanceState) {
    12         super.onCreate(savedInstanceState);
    13         requestWindowFeature(Window.FEATURE_NO_TITLE);
    14         setContentView(R.layout.activity_main);
    15 
    16         initContentList();
    17 
    18         mCustomLv = (CustomListView) findViewById(R.id.custom_lv);
    19         mCustomLv.setOnDeleteListener(new OnDeleteListener() {
    20 
    21             @Override
    22             public void onDelete(int index) {
    23                 contentList.remove(index);
    24                 mAdapter.notifyDataSetChanged();
    25             }
    26         });
    27 
    28         mAdapter = new CustomListViewAdapter(this, 0, contentList);
    29         mCustomLv.setAdapter(mAdapter);
    30     }
    31 
    32     // 初始化内容列表
    33     private void initContentList() {
    34         for (int i = 0; i < 20; i++) {
    35             contentList.add("内容项" + i);
    36         }
    37     }
    38 
    39     @Override
    40     public void onBackPressed() {
    41         if (mCustomLv.isDeleteShown()) {
    42             mCustomLv.hideDelete();
    43             return;
    44         }
    45         super.onBackPressed();
    46     }
    47 
    48 }
    复制代码

    7、运行效果如下:

    Refer:http://blog.csdn.net/guolin_blog/article/details/17357967

  • 相关阅读:
    [抄书]The Pipes and Filters pattern
    [抄书]The Layers pattern
    OpenGL Step by Step (1)
    [HOOPS]二维点向三维空间投影
    心仪已久的工具:BoundsChecker v7.2
    [HOOPS]用HC_Show_...获取正确的点的坐标位置
    小试zlib
    XML (2) Document Type Definitions (DTD)
    UML (1) 设计模式及作业附图
    XML (1) 什么是XML
  • 原文地址:https://www.cnblogs.com/xgjblog/p/6257323.html
Copyright © 2011-2022 走看看