热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

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

在毕设项目中多处用到自定义控件,一直打算总结一下自定义控件的实现方式,今天就来总结一下吧。在此之前学习了郭霖大神博客上面关于自定义View的

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

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

(一)组合控件

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

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

<&#63;xml version="1.0" encoding="utf-8"&#63;>


  

可见这个标题栏控件还是比较简单的,其中在左边有一个返回按钮,背景是一张事先准备好的图片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中引入自定义的标题栏:



  
  



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中引入该自定义布局:



  



3、运行效果如下:

(三)继承控件

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

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

<&#63;xml version="1.0" encoding="utf-8"&#63;>


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:

<&#63;xml version="1.0" encoding="utf-8"&#63;>


  



4、定义适配器类CustomListViewAdapter,继承自ArrayAdapter

public class CustomListViewAdapter extends ArrayAdapter {

  public CustomListViewAdapter(Context context, int textViewResourceId,
      List 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:



  



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

public class MainActivity extends Activity {

  // 自定义Lv
  private CustomListView mCustomLv;
  // 自定义适配器
  private CustomListViewAdapter mAdapter;
  // 内容列表
  private List cOntentList= new ArrayList();

  @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 listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
author-avatar
min覀igreenr
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有