Android之自定义ListView(一)
PS:自定义View是Android中高手进阶的路线.因此我也打算一步一步的学习.看了鸿洋和郭霖这两位大牛的博客,决定一步一步的学习,循序渐进.
学习内容:
1.自定义View实现ListView的Item左右滑动显示和隐藏弹窗的效果
自定义View其实是在Android学习路上比较难掌握的一个重要点,但是也是高手的必经之路,自定义View分为很多种,我们可以直接继承View,或者是继承他的直接子类或间接子类.ViewGroup,ListView,LinearLayout,Button等等.继承他们的间接子类还算是比较简单的..因为View的子类或者是间接子类可以帮助我们做很多的事情.有很多的地方,我们可以不去实现,子类就帮我们做了(onMeasure,onLayout,onDraw)这些方法.直接继承View,我们是必须要对这些方法进行重写的,来实现我们自定义View.
因此我这里就没有去直接继承View,而是选择继承了他的子类.由于ListView的使用还是相当的广泛的,也是有些费劲的.因此决定从ListView开始..这里先把自定义的ListView代码先粘出来.然后再一步一步的分析.
package com.view; import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;
import com.example.administrator.myview.R; /**
* Created by totem on 2016/7/10.
* @author 代码丶如风
*/
public class MyListView extends ListView { private static final String TAG = "ListView"; private LayoutInflater inflater; /**
* 手指按下的x,y坐标,以及移动以后的x,y坐标
*/
private int xDown; private int yDown; private int xMove; private int yMove; private boolean isRightSliding;
private boolean isLeftSliding; //滑动的最小距离
private int touchSlop; //PopWindow弹窗
private PopupWindow popupWindow;
private int popWindowWidth;
private int popWindowHeight; private Button delButton; private int mCurrentViewPosition;
private View mCurrentView; //回调接口
private DeleteItemListener deleteItemListener; /**
* 初始化操作
*/
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
inflater = LayoutInflater.from(context);
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
View view = inflater.inflate(R.layout.delete_item, null);
delButton = (Button) view.findViewById(R.id.id_item_btn);
popupWindow = new PopupWindow(view, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
popupWindow.getContentView().measure(0, 0);
popWindowWidth = popupWindow.getContentView().getMeasuredWidth();
popWindowHeight = popupWindow.getContentView().getMeasuredHeight();
} @Override
public boolean dispatchTouchEvent(MotionEvent event) {
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
xDown = x;
yDown = y;
if (popupWindow.isShowing()) {
dismissPopWindow();
}
mCurrentViewPosition = pointToPosition(xDown, yDown);
View view = getChildAt(mCurrentViewPosition - getFirstVisiblePosition());
mCurrentView = view;
break;
case MotionEvent.ACTION_MOVE:
xMove = x;
yMove = y;
int offsetX = xDown - xMove;
int offsetY = yDown - yMove;
if (xMove < xDown && Math.abs(offsetX) > touchSlop && Math.abs(offsetY) < touchSlop) {
isLeftSliding = true;
}else if(xMove >xDown && Math.abs(offsetX) >touchSlop && Math.abs(offsetY) < touchSlop){
isRightSliding = true;
}
break;
}
return super.dispatchTouchEvent(event);
} @Override
public boolean onTouchEvent(MotionEvent event) { int action = event.getAction();
if (isLeftSliding) {
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
int location[] = new int[2];
mCurrentView.getLocationOnScreen(location);
popupWindow.setAnimationStyle(R.style.popwindow_delete_btn_anim_style); //设置弹窗的动画效果
popupWindow.update();
popupWindow.showAtLocation(mCurrentView, Gravity.LEFT | Gravity.TOP, location[0] + mCurrentView.getWidth(), location[1] + mCurrentView.getHeight() / 2
- popWindowHeight / 2);
delButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (deleteItemListener != null) {
deleteItemListener.DeleteItem(mCurrentViewPosition);
popupWindow.dismiss();
}
}
});
break;
case MotionEvent.ACTION_UP:
isLeftSliding = false;
break;
}
//防止与Item点击事件冲突
return true;
}else if(isRightSliding){
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if(popupWindow.isShowing()){
dismissPopWindow();
}
break;
case MotionEvent.ACTION_UP:
isRightSliding = false;
break;
}
return true;
}
return super.onTouchEvent(event);
} public void setDeleteItemListener(DeleteItemListener listener) {
deleteItemListener = listener;
} public interface DeleteItemListener {
void DeleteItem(int position);
} private void dismissPopWindow() {
if (popupWindow != null && popupWindow.isShowing()) {
popupWindow.dismiss();
}
}
}
这里一部分是鸿洋大牛的.我又简单的优化了点.他写的只有向左滑动弹出,而没有向右滑动隐藏.因此我这里就给加上了.主要还是理解其中的思想才是关键.毕竟自定义View的学习没有什么相关的书籍,只能看这些牛人的博客了.看完也是受益匪浅的.废话不多说,我们总体缕一下思路,如何去实现这个自定义View是关键.
首先:
我们要清楚,用户的操作,怎样才算是滑动效果,怎样算是从左往右划,怎样算从右往左划,这也是问题的第一个关键所在,那么我们知道用户滑动的时候首先是需要点击屏幕的,因此这里定义了xDown和yDown来记录手指点下的坐标.那么滑动完以后,必然有一个结束滑动的坐标,xMove,yMove.相比到这里我们就明确怎样是向左滑动和向右滑动了.
xMove - xDown > 0 ? 向左滑动:向右滑动。。同时这里还要满足xMove - xDown要大于最小的滑动距离,这里最小的滑动距离就是用touchslop来记录的即:touchslop = ViewConfiguration.get(context).getScaledTouchSlop().这样我们就先解决了第一个问题,如何判断滑动.
其次:
滑动之后需要弹出一个窗口,那么这里就使用PopWindow来实现了.因此这里就创建了一个PopWindow.这个PopWindow可以显示出滑动后的删除按钮,因此在new的时候,需要把相关的view附加上.
/**
* <p>Create a new non focusable popup window which can display the
* <tt>contentView</tt>. The dimension of the window must be passed to
* this constructor.</p>
*
* <p>The popup does not provide any background. This should be handled
* by the content view.</p>
*
* @param contentView the popup's content
* @param width the popup's width
* @param height the popup's height
*/
public PopupWindow(View contentView, int width, int height) {
this(contentView, width, height, false);
}
源码是这样写的,创建一个无焦点的PopWindow用于显示我们传入的contentView.在这里这个PopWindow用于显示删除按钮.但是我们需要明确一个地方.这个PopWindow是我们new出来的,并没有在xml文件中进行书写,我们在获取它的宽高时,需要调用measure()方法,先对这个PopWindow进行测量,测量之后我们才能够拿到相应的宽度和高度,因为这个PopWindow并没有在我们的ListView中,也没有在Item中,而是我们手动加上的,因此ListView在onMeasure()的时候是不会对这个PopWindow进行测量的.这个取决于View的加载机制,这里我先不进行多说,等到后期我会补上View的加载机制,如果读者想现在弄明白怎么回事,推荐先去看看郭林大牛的博客,关于View的四篇文章,读完那四篇文章,就能够理解这块到底是怎么回事了.反正在这里读者只需要先记住就可以,不调用measure()方法是拿不到宽高的.读者可以自己去试一下.好了,这样我们就解决了第二个问题.
最后:回调接口
首先,我们在MyView中引入了一个按钮,也就是PopWindow显示的按钮,但是这个按钮需要做事情,它需要在被点击的时候移除掉当前的Item,我们知道只有主线程才有权利对View进行操作,我们定义的这个View是没有权利的,这样就需要一个回调接口,在Button被点击的时候出发回调事件,触发的时候需要传递position参数,也就是当前Item的position.这个position传入后,被称为登记回调函数.触发的同时登记回调函数告知主线程这个事件被触发了,需要主函数进行处理,那么主线程在对Item进行操作,这样就符合规则了,因此回调函数这个概念想必大家就清楚了.
前一阵子在知乎上看到有解释这个的概念,觉得说的非常的合理,在这里放上,这样就更方便大家的理解了.
回调函数是什么?
链接:http://www.zhihu.com/question/19801131/answer/13005983 来源:知乎
你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件.
然后事件分发机制了.dispatchTouchEvent和onTouchEvent,其实中间还有一个interpectTouchEvent,用于判断是否需要对事件进行拦截,这个就不说了,对ACTION_DOWN,ACTION_UP,ACTION_DOWN事件进行处理.dispatchTouchEvent用于分发事件,只在这里进行了简单的操作,对当前被点击的Item进行记录,以及对isRightSliding,isLeftSliding属性进行赋值,分发之后就交给了onTouchEvent去处理.它来完成对ACTION的处理.这样我们的总体思路就非常清晰了.
首先:对滑动事件的判断,其次:加入我们滑动后需要显示的效果,接着添加相应的回调函数用来处理View的触发事件,最后对事件分发处理,就解决了自定义View.最后附上MainActivity的源代码.
package com.example.administrator.myview; import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import com.view.MyListView;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; public class MainActivity extends Activity { private MyListView myListView;
private ArrayAdapter<String> adapter;
private List<String> mData; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
} private void initView(){
myListView = (MyListView) findViewById(R.id.MyListView);
mData = new ArrayList<>(Arrays.asList("1","2","3","4","5","6","7","8","9","10"));
adapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,mData);
myListView.setAdapter(adapter);
myListView.setDeleteItemListener(new MyListView.DeleteItemListener() {
@Override
public void DeleteItem(int position) {
adapter.remove(adapter.getItem(position));
}
});
myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//Item的点击事件
}
});
}
}
我们看到MainActivity实现回调接口,然后对View进行处理,与我们所说一致.这样就OK了,这里只简单的写了个Adapter,如果想写更复杂的就自己去实现Adapter了.
博客园的文件上传限制在10M,没有办法只能分享个百度云的链接了.
源代码:http://pan.baidu.com/s/1o8KVlXK
Android之自定义ListView(一)的更多相关文章
- Android复杂自定义Listview实现
在Android中实现Listview对新人来说比较难以理解,本人看了若干文章后觉得可以使用以下思路来让新人更好理解(同时也做好记录,免得自己以后忘记). 可参考博客:http://cinderell ...
- Android中自定义ListView实现上拉加载更多和下拉刷新
ListView是Android中一个功能强大而且很常用的控件,在很多App中都有ListView的下拉刷新数据和上拉加载更多这个功能.这里我就简单记录一下实现过程. 实现这个功能的方法不止一个,Gi ...
- Android中自定义ListView无法响应OnItemClickListener中的onItemClick方法问题解决方案
如果你的自定义ListViewItem中有Button或者Checkable的子类控件的话,那么默认focus是交给了子控件,而ListView 的Item能被选中的基础是它能获取Focus,也就是说 ...
- 【Android】自定义ListView的Adapter报空指针异常解决方法
刚刚使用ViewHolder的方法拉取ListView的数据,但是总会报异常.仔细查看代码,都正确. 后来打开adapter类,发现getView的返回值为null. 即return null. 将n ...
- Android 自定义ListView
本文讲实现一个自定义列表的Android程序,程序将实现一个使用自定义的适配器(Adapter)绑定 数据,通过contextView.setTag绑定数据有按钮的ListView. 系统显示列表(L ...
- [置顶] android 自定义ListView实现动画特效
通过自定义ListView实现动画特效,被点击元素A向前移,A之前元素往后移动. 重点在于动画的实现: 具体代码如下: package com.open.widget; import java.uti ...
- android自定义listview实现圆角
在项目中我们会经常遇到这种圆角效果,因为直角的看起来确实不那么雅观,可能大家会想到用图片实现,试想上中下要分别做三张图片,这样既会是自己的项目增大也会增加内存使用量,所以使用shape来实现不失为一种 ...
- Android中使用ListView绘制自定义表格(2)
上回再写了<Android中使用ListView绘制自定义表格>后,很多人留言代码不全和没有数据样例.但因为项目原因,没法把源码全部贴上来.近两天,抽空简化了一下,做了一个例子. 效果图如 ...
- android之ListView,详细介绍实现步骤,举例,自定义listview适配器
android之ListView,详细介绍实现步骤,举例,自定义listview适配器 本文来源于www.ifyao.com禁止转载!www.ifyao.com android中如何使用listVie ...
随机推荐
- RHEL5.8的NFS配置文件
RHEL5.8的NFS配置文件 路径:/etc/sysconfig/nfs # # Define which protocol versions mountd # will advertise. Th ...
- 当匿名类型遇上Distinct
首先定义一个简单类,并重写ToString方法. public class CommidityFilter { public string Property { get; set; } public ...
- 压力测试工具ab使用
ab全名是apache bench,是apache自带的一款压力测试工具.它通过创建多个线程来模拟并发,测试目标是基于URL的,因此不论是什么web服务器都可以支持. 使用ab非常简单,进入apach ...
- linux 2.6 驱动笔记(一)
本文作为linux 2.6 驱动笔记,记录环境搭建及linux基本内核模块编译加载. 环境搭建: 硬件:OK6410开发板 目标板操作系统:linux 2.6 交叉编译环境:windows 7 + v ...
- 【译】Activitys, Threads和 内存泄露
Android编程中一个共同的困难就是协调Activity的生命周期和长时间运行的任务(task),并且要避免可能的内存泄露.思考下面Activity的代码,在它启动的时候开启一个线程并循环执行任务. ...
- jQuery的动画处理总结
最近一年多一直在做前端的东西,由于老板在追求一些年轻动感的效果,让页面元素不能仅仅是简单的隐藏显示,所以经常会使用一些动画效果,发现jQuery的动画真心好用啊,把常用的几个总结一下,希望不再每次使用 ...
- 虚拟化平台cloudstack(3)——安装(下)
登录时用户名和密码错误 默认账号密码 admin/password 管理节点安装好后,初始化数据库,完了使用admin和password登陆提示用户名密码错误,查看日志: Fail to genera ...
- SWF READER 破解日志。
网上传闻swf reader是破解最厉害的神器,可以内存抓取+doSWF反编译.所以去官网下了一个: SWF_Reader_2.3 不出所料,demo版本没有反编译的功能.网上搜到一个哥们尝试了下: ...
- 如何在CRM系统中集成ActiveReports最终报表设计器
有时候,将ActiveReports设计器集成到业务系统中,为用户提供一些自定义的数据表,用户不需要了解如何底层的逻辑关系和后台代码,只需要选择几张关联的数据表,我们会根据用户的选择生成可供用户直接使 ...
- redis系列-redis的使用场景
redis越来越受大家欢迎,提升下速度,做下缓存,完成KPI之利器呀.翻译一篇文章<<How to take advantage of Redis just adding it to yo ...