文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android自定义View的三种实现方式总结

2022-06-06 04:25

关注

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

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

(一)组合控件

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

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


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:background="#0000ff" >
  <Button
    android:id="@+id/left_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_margin="5dp"
    android:background="@drawable/back1_64" />
  <TextView
    android:id="@+id/title_tv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:text="这是标题"
    android:textColor="#ffffff"
    android:textSize="20sp" />
</RelativeLayout>

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

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


public class TitleView extends RelativeLayout {
  // 返回按钮控件
  private Button mLeftBtn;
  // 标题Tv
  private TextView mTitleTv;
  public TitleView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // 加载布局
    LayoutInflater.from(context).inflate(R.layout.title_bar, this);
    // 获取控件
    mLeftBtn = (Button) findViewById(R.id.left_btn);
    mTitleTv = (TextView) findViewById(R.id.title_tv);
  }
  // 为左侧返回按钮添加自定义点击事件
  public void setLeftButtonListener(OnClickListener listener) {
    mLeftBtn.setOnClickListener(listener);
  }
  // 设置标题的方法
  public void setTitleText(String title) {
    mTitleTv.setText(title);
  }
}

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

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


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/main_layout"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical" >
  <com.example.test.TitleView
    android:id="@+id/title_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >
  </com.example.test.TitleView>
</LinearLayout>

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


private TitleView mTitleBar;
     mTitleBar = (TitleView) findViewById(R.id.title_bar);
    mTitleBar.setLeftButtonListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        Toast.makeText(MainActivity.this, "点击了返回按钮", Toast.LENGTH_SHORT)
            .show();
        finish();
      }
    });

5、运行效果如下:

   

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

 (二)自绘控件

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

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


public class CounterView extends View implements OnClickListener {
  // 定义画笔
  private Paint mPaint;
  // 用于获取文字的宽和高
  private Rect mBounds;
  // 计数值,每点击一次本控件,其值增加1
  private int mCount;
  public CounterView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // 初始化画笔、Rect
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mBounds = new Rect();
    // 本控件的点击事件
    setOnClickListener(this);
  }
  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mPaint.setColor(Color.BLUE);
    // 绘制一个填充色为蓝色的矩形
    canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
    mPaint.setColor(Color.YELLOW);
    mPaint.setTextSize(50);
    String text = String.valueOf(mCount);
    // 获取文字的宽和高
    mPaint.getTextBounds(text, 0, text.length(), mBounds);
    float textWidth = mBounds.width();
    float textHeight = mBounds.height();
    // 绘制字符串
    canvas.drawText(text, getWidth() / 2 - textWidth / 2, getHeight() / 2
        + textHeight / 2, mPaint);
  }
  @Override
  public void onClick(View v) {
    mCount ++;
    // 重绘
    invalidate();
  }
}

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


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/main_layout"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical" >
  <com.example.test.CounterView
    android:id="@+id/counter_view"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:layout_gravity="center_horizontal|top"
    android:layout_margin="20dp" />
</LinearLayout>

3、运行效果如下:

(三)继承控件

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

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


<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:background="#FF0000"
  android:padding="5dp"
  android:text="删除"
  android:textColor="#FFFFFF"
  android:textSize="16sp" >
</Button>

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


public class CustomListView extends ListView implements OnTouchListener,
    OnGestureListener {
  // 手势动作探测器
  private GestureDetector mGestureDetector;
  // 删除事件监听器
  public interface OnDeleteListener {
    void onDelete(int index);
  }
  private OnDeleteListener mOnDeleteListener;
  // 删除按钮
  private View mDeleteBtn;
  // 列表项布局
  private ViewGroup mItemLayout;
  // 选择的列表项
  private int mSelectedItem;
  // 当前删除按钮是否显示出来了
  private boolean isDeleteShown;
  public CustomListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // 创建手势监听器对象
    mGestureDetector = new GestureDetector(getContext(), this);
    // 监听onTouch事件
    setOnTouchListener(this);
  }
  // 设置删除监听事件
  public void setOnDeleteListener(OnDeleteListener listener) {
    mOnDeleteListener = listener;
  }
  // 触摸监听事件
  @Override
  public boolean onTouch(View v, MotionEvent event) {
    if (isDeleteShown) {
      hideDelete();
      return false;
    } else {
      return mGestureDetector.onTouchEvent(event);
    }
  }
  @Override
  public boolean onDown(MotionEvent e) {
    if (!isDeleteShown) {
      mSelectedItem = pointToPosition((int) e.getX(), (int) e.getY());
    }
    return false;
  }
  @Override
  public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
      float velocityY) {
    // 如果当前删除按钮没有显示出来,并且x方向滑动的速度大于y方向的滑动速度
    if (!isDeleteShown && Math.abs(velocityX) > Math.abs(velocityY)) {
      mDeleteBtn = LayoutInflater.from(getContext()).inflate(
          R.layout.delete_btn, null);
      mDeleteBtn.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          mItemLayout.removeView(mDeleteBtn);
          mDeleteBtn = null;
          isDeleteShown = false;
          mOnDeleteListener.onDelete(mSelectedItem);
        }
      });
      mItemLayout = (ViewGroup) getChildAt(mSelectedItem
          - getFirstVisiblePosition());
      RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
          LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
      params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
      params.addRule(RelativeLayout.CENTER_VERTICAL);
      mItemLayout.addView(mDeleteBtn, params);
      isDeleteShown = true;
    }
    return false;
  }
  // 隐藏删除按钮
  public void hideDelete() {
    mItemLayout.removeView(mDeleteBtn);
    mDeleteBtn = null;
    isDeleteShown = false;
  }
  public boolean isDeleteShown() {
    return isDeleteShown;
  }
  
  @Override
  public void onShowPress(MotionEvent e) {
  }
  @Override
  public boolean onSingleTapUp(MotionEvent e) {
    return false;
  }
  @Override
  public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
      float distanceY) {
    return false;
  }
  @Override
  public void onLongPress(MotionEvent e) {
  }
}

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


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:descendantFocusability="blocksDescendants" >
  <TextView
    android:id="@+id/content_tv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_margin="30dp"
    android:gravity="center_vertical|left" />
</RelativeLayout>

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


public class CustomListViewAdapter extends ArrayAdapter<String> {
  public CustomListViewAdapter(Context context, int textViewResourceId,
      List<String> objects) {
    super(context, textViewResourceId, objects);
  }
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    View view;
    if (convertView == null) {
      view = LayoutInflater.from(getContext()).inflate(
          R.layout.custom_listview_item, null);
    } else {
      view = convertView;
    }
    TextView contentTv = (TextView) view.findViewById(R.id.content_tv);
    contentTv.setText(getItem(position));
    return view;
  }
}

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


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/main_layout"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical" >
  <com.example.test.CustomListView
    android:id="@+id/custom_lv"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
</LinearLayout>

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


public class MainActivity extends Activity {
  // 自定义Lv
  private CustomListView mCustomLv;
  // 自定义适配器
  private CustomListViewAdapter mAdapter;
  // 内容列表
  private List<String> contentList = new ArrayList<String>();
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_main);
    initContentList();
    mCustomLv = (CustomListView) findViewById(R.id.custom_lv);
    mCustomLv.setOnDeleteListener(new OnDeleteListener() {
      @Override
      public void onDelete(int index) {
        contentList.remove(index);
        mAdapter.notifyDataSetChanged();
      }
    });
    mAdapter = new CustomListViewAdapter(this, 0, contentList);
    mCustomLv.setAdapter(mAdapter);
  }
  // 初始化内容列表
  private void initContentList() {
    for (int i = 0; i < 20; i++) {
      contentList.add("内容项" + i);
    }
  }
  @Override
  public void onBackPressed() {
    if (mCustomLv.isDeleteShown()) {
      mCustomLv.hideDelete();
      return;
    }
    super.onBackPressed();
  }
}

7、运行效果如下:

您可能感兴趣的文章:android TextView设置中文字体加粗实现方法android listview优化几种写法详细介绍android imageview图片居中技巧应用android开发教程之listview使用方法Android下拉刷新ListView——RTPullListView(demo)Android自定义Adapter的ListView的思路及代码android自定义进度条渐变色View的实例代码android实现上下滚动的TextViewAndroid中实现Webview顶部带进度条的方法Android中实现监听ScrollView滑动事件


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯