【android基础篇】自定义控件实现一些比较常见的功能

I,我要实现的效果

如图所示:

当下拉时,显示下拉刷新。松开后,显示松开刷新,接着正在刷新。

II,准备工作

1)需要自定义ListView,写一个类继承ListView。并在ListView中添加一些数据。

 1 private void initView() {
 2     listData = new ArrayList<String>();
 3         for (int i = 0; i <30; i++) {
 4             listData.add("我是来自仙桃的西藏哥,我真的很爱骑行......"+i);
 5         }
 6         ManualListView manualView= (ManualListView) findViewById(R.id.manual_view);
 7         manualView.setAdapter(new MyAdapter());
 8     }
 9
10     private class MyAdapter extends BaseAdapter{
11         @Override
12         public int getCount() {
13             return listData.size();
14         }
15         @Override
16         public Object getItem(int position) {
17
18             return null;
19         }
20         @Override
21         public long getItemId(int position) {
22             return 0;
23         }
24         @Override
25         public View getView(int position, View convertView, ViewGroup parent) {
26             TextView tv_word=new TextView(MainActivity.this);
27             tv_word.setTextSize(20);
28             tv_word.setText(listData.get(position));
29             return tv_word;
30         }
31     }

其实ListView中有两个方法值得关注:

manualView.addHeaderView(view)和manualView.addFooterView(view),这两个方法可以往ListView中去添加组件。

所以我们可以参照头部组件的样式写一个布局文件,并添加到ListView的头部。

下面是自定义ListView下初始化头部组件的方法:

1    /**
2      * 往ListView的头部添加组件。去写一个布局文件
3      */
4     public void initHeadView(){
5         View mHeadView=View.inflate(getContext(), R.layout.activity_head, null);
6         addHeaderView(mHeadView);
7     }

自定义的ListView中,实现父类的三个构造方法,并调用initHeadView方法。

这时候可以运行,可以看出,头部布局已经显现出来。

我们可以看出,头部组件中的progressBar的样式并不是我们所需要的样子,所以这里还需要自定义一下环形的progressBar。

II,自定义ProgressBar的样式

创建一个资源类型为drawable的文件,并且它的根节点为:shape

其中有两个属性:innerRadiusRatio表示的是内半径比,thicknessRation表示的是厚度比。

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <shape xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:shape="ring"
 4     android:innerRadiusRatio="2.5"
 5     android:thicknessRatio="10">
 6
 7     <gradient
 8         android:startColor="#ffffff"
 9         android:centerColor="#ff6666"
10         android:endColor="#ff0000"      android:type="sweep"
11         />
12 </shape>

gradient表示的颜色的变更,开始的颜色,中间的颜色,最后的颜色。type表示的是颜色渐变的程度。

由于ProgressBar需要实现旋转效果,所以还要有Rotate节点,最后写为:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <rotate xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:fromDegrees="0"
 4     android:toDegrees="360"
 5     android:pivotX="50%"
 6     android:pivotY="50%">
 7         <shape xmlns:android="http://schemas.android.com/apk/res/android"
 8         android:shape="ring"
 9         android:innerRadiusRatio="2.5"
10         android:thicknessRatio="10">
11         <gradient
12             android:startColor="#ffffff"
13             android:centerColor="#ff6666"
14             android:endColor="#ff0000"
15             android:type="sweep"
16             />
17         </shape>
18 </rotate>

其中fromDegress和toDegress分别表示是,旋转开始的结束的角度。pivotX和pivotY则表示旋转中心的位置。写为50%,表示位于圆心。

写好了布局文件后,就可以在ProgressBar中去关联。

android:indeterminateDrawable="@drawable/manual_progress"
indeterminate表示不确定之意。

运行后的结果:

progressBar的确实现了旋转,颜色变化的效果。

接下来要做的就是拉动ListView,就应该把头部组件隐藏掉。隐藏头部组件就不得不说,PaddingTop这个值。它表示的是和顶部的内边距。

当PaddingTop为正数的时候,那么头部组件的高度也就越来越大,为负数,则往上面移动,也就是慢慢的消失。

当为负的PrgressBar时,即头部组件刚刚消失在屏幕上。所以有:

1 public void initHeadView(){
2         mHeadView = View.inflate(getContext(), R.layout.activity_head, null);
3         mHeadView.measure(0, 0);
4         int mHeadHeight=mHeadView.getMeasuredHeight();
5         mHeadView.setPadding(0, -mHeadHeight, 0, 0);
6         addHeaderView(mHeadView);
7     }

mHeadView.measure(widthMeasureSpec, heightMeasureSpec)为什么设置为mHeadView.measure(0,0);

因为widthMeasureSpec和heightMeasureSpec表示的测量的规格,如果不给它规则,不指定它的宽和高,也就设置为0。

measureHeight = mHeadView.getMeasuredHeight();也就是拿到测量后的高度。

写OnTouchEvent事件:

首先判断,头部组件什么时候应该出现,什么时候应该隐藏。

 1 public boolean onTouchEvent(MotionEvent ev) {
 2         switch (ev.getAction()) {
 3         case MotionEvent.ACTION_DOWN:
 4
 5             break;
 6         case MotionEvent.ACTION_MOVE:
 7
 8             break;
 9         case MotionEvent.ACTION_UP:
10
11             break;
12         default:
13             break;
14         }
15         return super.onTouchEvent(ev);
16     }

MotionEvent.ACTION_DOWN表示手指按下时,MotionEvent.ACTION_MOVE表示手指移动时。我们可以分别的获取它们在Y轴上,也就是关于高度的值。

这时会有这几种情况,当MoveY-DownY为负值时,表示向上移动(因为安卓中,以屏幕的左上角为原点)。为正值时,表示向下移动。所以可以判断只有在下拉时,才显示

头部组件。但是还有一些情况,也要考虑,比如早LIstView的中间,无论上拉下拉都是ListView的条目时,是没有必要显示头部进度条的。所以这里可以判断,除了前面的条件外,

还得满足一个条件,即要是当前的ListView最前面的item的索引为0时,才下拉。此外,还有就是下拉的高度是什么?隐藏的头部组件的高度的负值加上移动的MoveY-DownY值就是下拉时移动的高度。于是就有:

 1 public boolean onTouchEvent(MotionEvent ev) {
 2         switch (ev.getAction()) {
 3         case MotionEvent.ACTION_DOWN:
 4             downY = (int) ev.getY();
 5             break;
 6         case MotionEvent.ACTION_MOVE:
 7             moveY = (int) ev.getY();
 8             int diffY=moveY-downY;
 9             if(diffY>0&&getFirstVisiblePosition()==0){
10                 int paddingTop=-mHeadHeight+diffY;
11                 mHeadView.setPadding(0, paddingTop, 0, 0);
12                 return true;
13             }
14             break;
15         case MotionEvent.ACTION_UP:
16             break;
17         default:
18             break;
19         }
20         return super.onTouchEvent(ev);
21     }

return true的意思是表示不让父类的方法响应触摸事件。

接下类来思考的问题是:什么时候是下拉刷新,什么时候是释放数显,什么时候是正在刷新?

我们可以根据paddingTop值来判断,当paddingTop小于0表示头部组件还未全部进入屏幕时,显示下拉刷新,大于0表示已经进入屏幕,显示释放刷新。

当时这里依然会存在一些问题,比如当你已经进入释放刷新的状态,还在刷新时,会不断的执行paddingTop大于0的语句。所以这里需要写些状态来区分,达到只执行一次的结果。代码:

 1     public boolean onTouchEvent(MotionEvent event) {
 2         switch (event.getAction()) {
 3         case MotionEvent.ACTION_MOVE:
 4             moveY = (int) event.getY();
 5             /**
 6              * 计算出移动的间距。向上滑动时,应该显示原生的。
 7              * moveY-downY如果计算的是负值,那么就是向上移动的。还有就是在ListView中间拉动时,也不应该拉出头布局。
 8              * 这两种情况都应该相应ListView的原生的状态。
 9              *
10              * 如果是正数,那就是向下移动。并且ListView顶部的条目索引为0时。
11              */
12             int diffY=moveY-downY;
13             if(diffY>0&&getFirstVisiblePosition()==0){
14                 /**
15                  * 执行下拉操作
16                  */
17                 int paddingTop=-measureHeight+diffY;
18                 /**
19                  *因为我不断的下拉的话,会不断的进入到某个状态,所以这里设置几个值来代表区分状态。
20                  */
21                 if(paddingTop>0&&currentState!=RELEASE_REFRESH){
22                     //进入释放刷新状态
23                     System.out.println("进入释放刷新状态");
24                     currentState=RELEASE_REFRESH;
25                 }else if(paddingTop<0&&currentState!=PULL_DOWN){
26                     //进入到下拉刷新状态
27                     System.out.println("进入到下拉刷新状态");
28                     currentState=PULL_DOWN;
29                 }
30                 mHeadView.setPadding(0, paddingTop, 0, 0);
31                 //不让父类的ListView响应事件,自己来处理......
32                 return true;
33             }
34             break;
35         case MotionEvent.ACTION_UP:
36             break;
37         case MotionEvent.ACTION_DOWN:
38             downY = (int) event.getY();
39             break;
40         default:
41             break;
42         }
43         /**
44          * super.onTouchEvent(event);表示的是原生的滑动事件
45          */
46         return super.onTouchEvent(event);
47     }

PULL_DOWN表示的是下拉刷新状态,而RELEASE_REFRESH表示释放刷新状态。

现在我们就可以根据状态来确定头部组件显示的文字。

时间: 2024-07-28 17:31:43

【android基础篇】自定义控件实现一些比较常见的功能的相关文章

cocos2dx基础篇(5)——浅析几个常见类

目录 入口类main.cpp 主要控制类AppDelegate.cpp 节点类CCNode 导演类CCDirector 场景类CCScene 层次类CCLayer 精灵类CCSprite 大小类CCSize 坐标CCPoint 矩形类CCRect 数组类CCArray 入口类main.cpp 这是应用程序的入口类,用于创建cocos2dx的AppDelegate实例.窗口大小.以及运行程序. 主要代码如下:     // create the application instance     A

Android基础之——CountDownTimer类,轻松实现倒计时功能

在发现这个类之前,一直是用的handler,子线程发消息,UI线程进行倒计时的显示工作.前几天在做一个倒计时显示的时候发现了这个类,用起来很方便 翻看了下源码,内部已经帮我们实现了handler的子线程操作 CountDownTimer这个类用起来很简单,两个参数,几句代码搞定,如下: CountDownTimer(long millisInFuture, long countDownInterval) 构造函数有两个参数,第一个millisInFuture是指要倒计时的总时间,单位是long

android基础篇学习心得

android技术中,线程.进程.JNI.IPC和各个小框架结构是基本功.在跟随高焕堂老师的android程序猿到架构师之路系列视频中 学习完基础篇之后,颇有些心得,记录下来. android开发就是app开发吗?曾听过很多人说android学习很简单,做个app轻松就上手了.我一直觉得不以为然,许多程序员做app开发时,心中大致只有四大组件.各种布局.数据库和一些常用控件.对于各小框架.线程间通信.绑定服务.java与c的对接等基本原理并不清楚,也没必要弄清楚. 最近学校搞了一个实训,要求做一

【Android基础篇】使用ExpandableListView实现自定义的下拉列表

1. ExpandableListView简介 下拉列表(可扩展的列表控件)在App应用非常常见,在Android开发中是我们必须掌握的一个控件,下面就来介绍一下ExpandableListView这个控件的开发. ExpandableListView分为组列表项和子列表项,单击组列表项,会显示这组里所有的子列表项.和ListView一样,它也是通过Adapter数据适配器完成数据与显示的衔接,但它使用的另一种接口:ExpandableListAdapter. 今天我们要做的是实现一个继承它的父

Android 基础篇(二)

ADB进程 adb指令 adb install xxx.apk adb uninstall 包名 adb devices adb start-server adb kill-server adb shell –> 进入Linux的命令行 ls –> 显示文件及包名 ps –> 列举所有运行的进程 rm –> 删除文件 cd –> 切换目录 adb push 文件路径/文件名 sdcard/文件名 –> 把文件放进去 adb pull sdcard/文件名 –> 把

【Android基础篇】AutoCompleteTextView和MultiAutoCompleteTextView

从名称上可看出来,这两个控件都是用于输入信息的TextView,AutoComplete已表明这两个控件内容输入都是自动完成的.区别在于一个是Multi,一个不是,具体的区别可通过下面的内容看出来.下面分别介绍着两个控件的使用. AutoCompleteTextView 功能 动态匹配输入的内容,如搜索引擎在输入框输入信息时,会有一个下拉列表显示与当前输入内容有关的信息. 控件特有属性 如同width.height等属性是控件共有属性,下面介绍AutoCompleteTextView特有的属性:

Android基础篇之Android快速入门--你必须要知道的基础

Android快速入门 1. 搭建开发环境 >解压压缩文件,得到:①Android SDK   (类似于JDK)② Eclipse  ③ADT >配置两个path环境变量:D:\adt-bundle-windows-x86\sdk\platform-tools:D:\adt-bundle-windows-x86\sdk\tools >配置基本的Eclipse的设置: 调整字体大小,字符集,配置android sdk的位置 >创建模拟器: 2. 创建第一个Android项目: Hel

从0开始不断温习,Android基础篇

(^▽^)经常发现学着学着,由于学习的东西越来越多,接触的东西越来越多,逐渐的吧自己的最基础的东西忘得差不多了(o(╥﹏╥)o我也差不多忘了很多东西了)发现越优秀的人 越注重细节,基础更加扎实和巩固 分享一下自己整理的面试学习路线 请查看完整的PDF版(更多完整项目下载.未完待续.源码.图文知识后续上传github.)可以点击关于我联系我获取完整PDF(VX:mm14525201314) 一.Activity 是什么? Activity 实际上只是一个与用户交互的接口而已 二.Activity

【Android基础篇】Activity的生命周期

Google开发者文档 http://developer.android.com/reference/android/app/Activity.html 应用的每个页面都是一个Activity,这些Activity都存放在一个Activity栈中,每次进入某个应用或者应用的某个界面时,新界面这个Activity就压入到Activity栈的顶部:退出或返回的时候就把栈顶的Activity弹出,之前的Activity就又到了栈顶. Activity生命周期里的四个状态 - Running : Act